diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2014-03-26 19:21:20 +0000 |
---|---|---|
committer | <> | 2014-05-08 15:03:54 +0000 |
commit | fb123f93f9f5ce42c8e5785d2f8e0edaf951740e (patch) | |
tree | c2103d76aec5f1f10892cd1d3a38e24f665ae5db /src/VBox/Runtime/r3/win/localipc-win.cpp | |
parent | 58ed4748338f9466599adfc8a9171280ed99e23f (diff) | |
download | VirtualBox-master.tar.gz |
Imported from /home/lorry/working-area/delta_VirtualBox/VirtualBox-4.3.10.tar.bz2.HEADVirtualBox-4.3.10master
Diffstat (limited to 'src/VBox/Runtime/r3/win/localipc-win.cpp')
-rw-r--r-- | src/VBox/Runtime/r3/win/localipc-win.cpp | 782 |
1 files changed, 688 insertions, 94 deletions
diff --git a/src/VBox/Runtime/r3/win/localipc-win.cpp b/src/VBox/Runtime/r3/win/localipc-win.cpp index 15ff6f8b..a25b9cac 100644 --- a/src/VBox/Runtime/r3/win/localipc-win.cpp +++ b/src/VBox/Runtime/r3/win/localipc-win.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -41,15 +41,17 @@ #include <Windows.h> #include <sddl.h> -#include <iprt/localipc.h> -#include <iprt/thread.h> -#include <iprt/critsect.h> #include <iprt/alloc.h> +#include <iprt/asm.h> #include <iprt/assert.h> -#include <iprt/param.h> +#include <iprt/critsect.h> #include <iprt/err.h> +#include <iprt/ldr.h> +#include <iprt/localipc.h> +#include <iprt/param.h> #include <iprt/string.h> -#include <iprt/asm.h> +#include <iprt/thread.h> +#include <iprt/time.h> #include "internal/magics.h" @@ -66,7 +68,9 @@ * * Note! FILE_GENERIC_WRITE (SDDL_FILE_WRITE) is evil here because it includes * the FILE_CREATE_PIPE_INSTANCE(=FILE_APPEND_DATA) flag. Thus the hardcoded - * value 0x0012019b in the 2nd ACE. It expands to: + * value 0x0012019b in the client ACE. The server-side still needs + * setting FILE_CREATE_PIPE_INSTANCE although. + * It expands to: * 0x00000001 - FILE_READ_DATA * 0x00000008 - FILE_READ_EA * 0x00000080 - FILE_READ_ATTRIBUTES @@ -75,11 +79,12 @@ * 0x00000002 - FILE_WRITE_DATA * 0x00000010 - FILE_WRITE_EA * 0x00000100 - FILE_WRITE_ATTRIBUTES - * 0x0012019b - * or FILE_GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_CREATE_PIPE_INSTANCE) + * = 0x0012019b (client) + * + (only for server): + * 0x00000004 - FILE_CREATE_PIPE_INSTANCE + * = 0x0012019f * - * @todo Double check this! - * @todo Drop the EA rights too? Since they doesn't mean anything to PIPS according to the docs. + * @todo Triple check this! * @todo EVERYONE -> AUTHENTICATED USERS or something more appropriate? * @todo Have trouble allowing the owner FILE_CREATE_PIPE_INSTANCE access, so for now I'm hacking * it just to get progress - the service runs as local system. @@ -88,12 +93,19 @@ * is to go the annoying route of OpenProcessToken, QueryTokenInformation, * ConvertSidToStringSid and then use the result... Suggestions are very welcome */ -#define RTLOCALIPC_WIN_SDDL \ - SDDL_DACL SDDL_DELIMINATOR \ +#define RTLOCALIPC_WIN_SDDL_BASE \ + SDDL_DACL SDDL_DELIMINATOR \ SDDL_ACE_BEGIN SDDL_ACCESS_DENIED ";;" SDDL_GENERIC_ALL ";;;" SDDL_NETWORK SDDL_ACE_END \ - SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED ";;" "0x0012019b" ";;;" SDDL_EVERYONE SDDL_ACE_END \ SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED ";;" SDDL_FILE_ALL ";;;" SDDL_LOCAL_SYSTEM SDDL_ACE_END +#define RTLOCALIPC_WIN_SDDL_SERVER \ + RTLOCALIPC_WIN_SDDL_BASE \ + SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED ";;" "0x0012019f" ";;;" SDDL_EVERYONE SDDL_ACE_END + +#define RTLOCALIPC_WIN_SDDL_CLIENT \ + RTLOCALIPC_WIN_SDDL_BASE \ + SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED ";;" "0x0012019b" ";;;" SDDL_EVERYONE SDDL_ACE_END + // SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED ";;" SDDL_GENERIC_ALL ";;;" SDDL_PERSONAL_SELF SDDL_ACE_END \ // SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED ";CIOI;" SDDL_GENERIC_ALL ";;;" SDDL_CREATOR_OWNER SDDL_ACE_END // SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED ";;" "0x0012019b" ";;;" SDDL_EVERYONE SDDL_ACE_END @@ -138,24 +150,41 @@ typedef RTLOCALIPCSERVERINT *PRTLOCALIPCSERVERINT; typedef struct RTLOCALIPCSESSIONINT { /** The magic (RTLOCALIPCSESSION_MAGIC). */ - uint32_t u32Magic; + uint32_t u32Magic; /** Critical section protecting the structure. */ - RTCRITSECT CritSect; + RTCRITSECT CritSect; /** The number of references to the instance. * @remarks The reference counting isn't race proof. */ - uint32_t volatile cRefs; + uint32_t volatile cRefs; + /** 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; /** Indicates that there is a pending cancel request. */ - bool volatile fCancelled; + bool volatile fCancelled; /** The name pipe handle. */ - HANDLE hNmPipe; + HANDLE hNmPipe; /** The handle to the event object we're using for overlapped I/O. */ - HANDLE hEvent; + HANDLE hEvent; /** The overlapped I/O structure. */ - OVERLAPPED OverlappedIO; + OVERLAPPED OverlappedIO; + /** Bounce buffer for writes. */ + uint8_t *pbBounceBuf; + /** Amount of used buffer space. */ + size_t cbBounceBufUsed; + /** Amount of allocated buffer space. */ + size_t cbBounceBufAlloc; + /** Buffer for the zero byte read. + * Used in RTLocalIpcSessionWaitForData(). */ + uint8_t abBuf[8]; } RTLOCALIPCSESSIONINT; /** Pointer to a local IPC session instance (Windows). */ typedef RTLOCALIPCSESSIONINT *PRTLOCALIPCSESSIONINT; +typedef BOOL WINAPI FNCONVERTSTRINGSECURITYDESCRIPTORTOSECURITYDESCRIPTOR(LPCTSTR, DWORD, PSECURITY_DESCRIPTOR, PULONG); +typedef FNCONVERTSTRINGSECURITYDESCRIPTORTOSECURITYDESCRIPTOR + *PFNCONVERTSTRINGSECURITYDESCRIPTORTOSECURITYDESCRIPTOR; /* No, nobody fell on the keyboard, really! */ + /******************************************************************************* * Internal Functions * @@ -164,52 +193,124 @@ static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSION phClientSession, HANDLE /** + * Builds and allocates the security descriptor required for securing the local pipe. + * + * @return IPRT status code. + * @param ppDesc Where to store the allocated security descriptor on success. + * Must be free'd using LocalFree(). + */ +static int rtLocalIpcServerWinAllocSecurityDescriptior(PSECURITY_DESCRIPTOR *ppDesc, bool fServer) +{ + /** @todo Stuff this into RTInitOnce? Later. */ + PFNCONVERTSTRINGSECURITYDESCRIPTORTOSECURITYDESCRIPTOR + pfnConvertStringSecurityDescriptorToSecurityDescriptor = NULL; + + RTLDRMOD hAdvApi32 = NIL_RTLDRMOD; + int rc = RTLdrLoadSystem("Advapi32.dll", true /*fNoUnload*/, &hAdvApi32); + if (RT_SUCCESS(rc)) + rc = RTLdrGetSymbol(hAdvApi32, "ConvertStringSecurityDescriptorToSecurityDescriptorW", + (void**)&pfnConvertStringSecurityDescriptorToSecurityDescriptor); + + PSECURITY_DESCRIPTOR pSecDesc = NULL; + if (RT_SUCCESS(rc)) + { + AssertPtr(pfnConvertStringSecurityDescriptorToSecurityDescriptor); + + /* + * We'll create a security descriptor from a SDDL that denies + * access to network clients (this is local IPC after all), it + * makes some further restrictions to prevent non-authenticated + * users from screwing around. + */ + PRTUTF16 pwszSDDL; + rc = RTStrToUtf16(fServer + ? RTLOCALIPC_WIN_SDDL_SERVER : RTLOCALIPC_WIN_SDDL_CLIENT, &pwszSDDL); + if (RT_SUCCESS(rc)) + { + if (!pfnConvertStringSecurityDescriptorToSecurityDescriptor((LPCTSTR)pwszSDDL, + SDDL_REVISION_1, + &pSecDesc, + NULL)) + { + rc = RTErrConvertFromWin32(GetLastError()); + } + + RTUtf16Free(pwszSDDL); + } + } + else + { + /* Windows OSes < W2K SP2 not supported for now, bail out. */ + /** @todo Implement me! */ + rc = VERR_NOT_SUPPORTED; + } + + if (hAdvApi32 != NIL_RTLDRMOD) + RTLdrClose(hAdvApi32); + + if (RT_SUCCESS(rc)) + { + AssertPtr(pSecDesc); + *ppDesc = pSecDesc; + } + + return rc; +} + +/** * Creates a named pipe instance. * * This is used by both RTLocalIpcServerCreate and RTLocalIpcServerListen. * - * @returns Windows error code, that is NO_ERROR and *phNmPipe on success and some ERROR_* on failure. - * + * @return IPRT status code. * @param phNmPipe Where to store the named pipe handle on success. This * will be set to INVALID_HANDLE_VALUE on failure. * @param pszFullPipeName The full named pipe name. * @param fFirst Set on the first call (from RTLocalIpcServerCreate), otherwise clear. * Governs the FILE_FLAG_FIRST_PIPE_INSTANCE flag. */ -static DWORD rtLocalIpcServerWinCreatePipeInstance(PHANDLE phNmPipe, const char *pszFullPipeName, bool fFirst) +static int rtLocalIpcServerWinCreatePipeInstance(PHANDLE phNmPipe, const char *pszFullPipeName, bool fFirst) { *phNmPipe = INVALID_HANDLE_VALUE; - /* - * We'll create a security descriptor from a SDDL that denies - * access to network clients (this is local IPC after all), it - * makes some further restrictions to prevent non-authenticated - * users from screwing around. - */ - DWORD err; - PSECURITY_DESCRIPTOR pSecDesc = NULL; -#if 0 /** @todo dynamically resolve this as it is the only thing that prevents - * loading IPRT on NT4. */ - if (ConvertStringSecurityDescriptorToSecurityDescriptor(RTLOCALIPC_WIN_SDDL, - SDDL_REVISION_1, - &pSecDesc, - NULL)) -#else - AssertFatalFailed(); - SetLastError(-1); - if (0) -#endif + PSECURITY_DESCRIPTOR pSecDesc; + int rc = rtLocalIpcServerWinAllocSecurityDescriptior(&pSecDesc, fFirst /* Server? */); + if (RT_SUCCESS(rc)) { SECURITY_ATTRIBUTES SecAttrs; - SecAttrs.nLength = sizeof(SecAttrs); + SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES); SecAttrs.lpSecurityDescriptor = pSecDesc; SecAttrs.bInheritHandle = FALSE; DWORD fOpenMode = PIPE_ACCESS_DUPLEX | PIPE_WAIT | FILE_FLAG_OVERLAPPED; - if (fFirst) - fOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE; /* Note! Requires W2K SP2+. */ + + bool fSupportsFirstInstance = false; + + OSVERSIONINFOEX OSInfoEx; + RT_ZERO(OSInfoEx); + OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if ( GetVersionEx((LPOSVERSIONINFO) &OSInfoEx) + && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + if ( /* Vista+. */ + OSInfoEx.dwMajorVersion >= 6 + /* Windows XP+. */ + || ( OSInfoEx.dwMajorVersion == 5 + && OSInfoEx.dwMinorVersion > 0) + /* Windows 2000. */ + || ( OSInfoEx.dwMajorVersion == 5 + && OSInfoEx.dwMinorVersion == 0 + && OSInfoEx.wServicePackMajor >= 2)) + { + /* Requires at least W2K (5.0) SP2+. This is non-fatal. */ + fSupportsFirstInstance = true; + } + } + + if (fFirst && fSupportsFirstInstance) + fOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE; HANDLE hNmPipe = CreateNamedPipe(pszFullPipeName, /* lpName */ fOpenMode, /* dwOpenMode */ @@ -219,19 +320,16 @@ static DWORD rtLocalIpcServerWinCreatePipeInstance(PHANDLE phNmPipe, const char PAGE_SIZE, /* nInBufferSize (ditto) */ 30*1000, /* nDefaultTimeOut = 30 sec */ &SecAttrs); /* lpSecurityAttributes */ - err = GetLastError(); LocalFree(pSecDesc); if (hNmPipe != INVALID_HANDLE_VALUE) { *phNmPipe = hNmPipe; - return NO_ERROR; } + else + rc = RTErrConvertFromWin32(GetLastError()); } - else - err = GetLastError(); - AssertReturn(err != NO_ERROR, ERROR_GEN_FAILURE); - return err; + return rc; } @@ -244,8 +342,7 @@ RTDECL(int) RTLocalIpcServerCreate(PRTLOCALIPCSERVER phServer, const char *pszNa AssertPtrReturn(pszName, VERR_INVALID_POINTER); AssertReturn(*pszName, VERR_INVALID_PARAMETER); AssertReturn(!(fFlags & ~(RTLOCALIPC_FLAGS_VALID_MASK)), VERR_INVALID_PARAMETER); - - AssertReturn(fFlags & RTLOCALIPC_FLAGS_MULTI_SESSION, VERR_NOT_IMPLEMENTED); /** @todo implement !RTLOCALIPC_FLAGS_MULTI_SESSION */ + AssertReturn((fFlags & RTLOCALIPC_FLAGS_MULTI_SESSION), VERR_INVALID_PARAMETER); /** @todo Implement !RTLOCALIPC_FLAGS_MULTI_SESSION */ /* * Allocate and initialize the instance data. @@ -263,24 +360,30 @@ RTDECL(int) RTLocalIpcServerCreate(PRTLOCALIPCSERVER phServer, const char *pszNa int rc = RTCritSectInit(&pThis->CritSect); if (RT_SUCCESS(rc)) { - DWORD err = NO_ERROR; - pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/, FALSE /*bInitialState*/, NULL /*lpName*/); + pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/, + FALSE /*bInitialState*/, NULL /*lpName*/); if (pThis->hEvent != NULL) { - memset(&pThis->OverlappedIO, 0, sizeof(pThis->OverlappedIO)); + RT_ZERO(pThis->OverlappedIO); pThis->OverlappedIO.Internal = STATUS_PENDING; pThis->OverlappedIO.hEvent = pThis->hEvent; - err = rtLocalIpcServerWinCreatePipeInstance(&pThis->hNmPipe, pThis->szName, true /* fFirst */); - if (err == NO_ERROR) + rc = rtLocalIpcServerWinCreatePipeInstance(&pThis->hNmPipe, + pThis->szName, true /* fFirst */); + if (RT_SUCCESS(rc)) { *phServer = pThis; return VINF_SUCCESS; } + + BOOL fRc = CloseHandle(pThis->hEvent); + AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); } else - err = GetLastError(); - rc = RTErrConvertFromWin32(err); + rc = RTErrConvertFromWin32(GetLastError()); + + int rc2 = RTCritSectDelete(&pThis->CritSect); + AssertRC(rc2); } RTMemFree(pThis); return rc; @@ -327,9 +430,10 @@ RTDECL(int) RTLocalIpcServerDestroy(RTLOCALIPCSERVER hServer) RTCritSectEnter(&pThis->CritSect); ASMAtomicUoWriteU32(&pThis->u32Magic, ~RTLOCALIPCSERVER_MAGIC); ASMAtomicUoWriteBool(&pThis->fCancelled, true); + Assert(pThis->cRefs); pThis->cRefs--; - if (pThis->cRefs > 0) + if (pThis->cRefs) { BOOL fRc = SetEvent(pThis->hEvent); AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); @@ -371,23 +475,23 @@ RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION /* * Try connect a client. We need to use overlapped I/O here because - * of the cancellation a by RTLocalIpcServerCancel and RTLocalIpcServerDestroy. + * of the cancellation done by RTLocalIpcServerCancel and RTLocalIpcServerDestroy. */ SetLastError(NO_ERROR); BOOL fRc = ConnectNamedPipe(pThis->hNmPipe, &pThis->OverlappedIO); - DWORD err = fRc ? NO_ERROR : GetLastError(); + DWORD dwErr = fRc ? NO_ERROR : GetLastError(); if ( !fRc - && err == ERROR_IO_PENDING) + && dwErr == ERROR_IO_PENDING) { WaitForSingleObject(pThis->hEvent, INFINITE); DWORD dwIgnored; fRc = GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &dwIgnored, FALSE /* bWait*/); - err = fRc ? NO_ERROR : GetLastError(); + dwErr = fRc ? NO_ERROR : GetLastError(); } RTCritSectEnter(&pThis->CritSect); - if ( !pThis->fCancelled - && pThis->u32Magic == RTLOCALIPCSERVER_MAGIC) + if ( !pThis->fCancelled /* Event signalled but not cancelled? */ + && pThis->u32Magic == RTLOCALIPCSERVER_MAGIC) { /* * Still alive, some error or an actual client. @@ -396,12 +500,12 @@ RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION * replaces the current one for the server. The current pipe instance * will be assigned to the client session. */ - if ( fRc - || err == ERROR_PIPE_CONNECTED) + if ( fRc + || dwErr == ERROR_PIPE_CONNECTED) { HANDLE hNmPipe; - DWORD err = rtLocalIpcServerWinCreatePipeInstance(&hNmPipe, pThis->szName, false /* fFirst */); - if (err == NO_ERROR) + rc = rtLocalIpcServerWinCreatePipeInstance(&hNmPipe, pThis->szName, false /* fFirst */); + if (RT_SUCCESS(rc)) { HANDLE hNmPipeSession = pThis->hNmPipe; /* consumed */ pThis->hNmPipe = hNmPipe; @@ -413,13 +517,12 @@ RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION * We failed to create a new instance for the server, disconnect * the client and fail. Don't try service the client here. */ - rc = RTErrConvertFromWin32(err); fRc = DisconnectNamedPipe(pThis->hNmPipe); AssertMsg(fRc, ("%d\n", GetLastError())); } } else - rc = RTErrConvertFromWin32(err); + rc = RTErrConvertFromWin32(dwErr); } else { @@ -430,9 +533,9 @@ RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION * in the this thread) or disconnect the client. */ if ( fRc - || err == ERROR_PIPE_CONNECTED) + || dwErr == ERROR_PIPE_CONNECTED) fRc = DisconnectNamedPipe(pThis->hNmPipe); - else if (err == ERROR_IO_PENDING) + else if (dwErr == ERROR_IO_PENDING) fRc = CancelIo(pThis->hNmPipe); else fRc = TRUE; @@ -464,15 +567,17 @@ RTDECL(int) RTLocalIpcServerCancel(RTLOCALIPCSERVER hServer) * Enter the critical section, then set the cancellation flag * and signal the event (to wake up anyone in/at WaitForSingleObject). */ - RTCritSectEnter(&pThis->CritSect); - - ASMAtomicUoWriteBool(&pThis->fCancelled, true); - BOOL fRc = SetEvent(pThis->hEvent); - AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + ASMAtomicUoWriteBool(&pThis->fCancelled, true); + BOOL fRc = SetEvent(pThis->hEvent); + AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); - RTCritSectLeave(&pThis->CritSect); + rc = RTCritSectLeave(&pThis->CritSect); + } - return VINF_SUCCESS; + return rc; } @@ -487,6 +592,9 @@ RTDECL(int) RTLocalIpcServerCancel(RTLOCALIPCSERVER hServer) */ static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSION phClientSession, HANDLE hNmPipeSession) { + AssertPtrReturn(phClientSession, VERR_INVALID_POINTER); + AssertReturn(hNmPipeSession != INVALID_HANDLE_VALUE, VERR_INVALID_HANDLE); + int rc; /* @@ -498,15 +606,18 @@ static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSION phClientSession, HANDLE pThis->u32Magic = RTLOCALIPCSESSION_MAGIC; pThis->cRefs = 1; /* our ref */ pThis->fCancelled = false; + pThis->fIOPending = false; + pThis->fZeroByteRead = false; pThis->hNmPipe = hNmPipeSession; rc = RTCritSectInit(&pThis->CritSect); if (RT_SUCCESS(rc)) { - pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/, FALSE /*bInitialState*/, NULL /*lpName*/); + pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/, + FALSE /*bInitialState*/, NULL /*lpName*/); if (pThis->hEvent != NULL) { - memset(&pThis->OverlappedIO, 0, sizeof(pThis->OverlappedIO)); + RT_ZERO(pThis->OverlappedIO); pThis->OverlappedIO.Internal = STATUS_PENDING; pThis->OverlappedIO.hEvent = pThis->hEvent; @@ -528,10 +639,88 @@ static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSION phClientSession, HANDLE return rc; } - RTDECL(int) RTLocalIpcSessionConnect(PRTLOCALIPCSESSION phSession, const char *pszName, uint32_t fFlags) { - return VINF_SUCCESS; + AssertPtrReturn(phSession, VERR_INVALID_POINTER); + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + AssertReturn(*pszName, VERR_INVALID_PARAMETER); + AssertReturn(!fFlags, VERR_INVALID_PARAMETER); /* Flags currently unused, must be 0. */ + + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAlloc(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + + pThis->u32Magic = RTLOCALIPCSESSION_MAGIC; + pThis->cRefs = 1; /* The one we return. */ + pThis->fIOPending = false; + pThis->fZeroByteRead = false; + pThis->fCancelled = false; + pThis->pbBounceBuf = NULL; + pThis->cbBounceBufAlloc = 0; + pThis->cbBounceBufUsed = 0; + + int rc = RTCritSectInit(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/, + FALSE /*bInitialState*/, NULL /*lpName*/); + if (pThis->hEvent != NULL) + { + RT_ZERO(pThis->OverlappedIO); + pThis->OverlappedIO.Internal = STATUS_PENDING; + pThis->OverlappedIO.hEvent = pThis->hEvent; + + PSECURITY_DESCRIPTOR pSecDesc; + rc = rtLocalIpcServerWinAllocSecurityDescriptior(&pSecDesc, false /* Client */); + if (RT_SUCCESS(rc)) + { + char *pszPipe; + if (RTStrAPrintf(&pszPipe, "%s%s", RTLOCALIPC_WIN_PREFIX, pszName)) + { + SECURITY_ATTRIBUTES SecAttrs; + SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES); + SecAttrs.lpSecurityDescriptor = pSecDesc; + SecAttrs.bInheritHandle = FALSE; + + HANDLE hPipe = CreateFile(pszPipe, /* pipe name */ + GENERIC_READ /* read and write access */ + | GENERIC_WRITE, + 0, /* no sharing */ + &SecAttrs, /* lpSecurityAttributes */ + OPEN_EXISTING, /* opens existing pipe */ + FILE_FLAG_OVERLAPPED, /* default attributes */ + NULL); /* no template file */ + RTStrFree(pszPipe); + + if (hPipe != INVALID_HANDLE_VALUE) + { + LocalFree(pSecDesc); + + pThis->hNmPipe = hPipe; + *phSession = pThis; + return VINF_SUCCESS; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else + rc = VERR_NO_MEMORY; + + LocalFree(pSecDesc); + } + + BOOL fRc = CloseHandle(pThis->hEvent); + AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); + } + else + rc = RTErrConvertFromWin32(GetLastError()); + + int rc2 = RTCritSectDelete(&pThis->CritSect); + AssertRC(rc2); + } + + RTMemFree(pThis); + return rc; } @@ -564,9 +753,9 @@ RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession) */ if (hSession == NIL_RTLOCALIPCSESSION) return VINF_SUCCESS; - PRTLOCALIPCSESSIONINT pThis = (RTLOCALIPCSESSION)hSession; + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; AssertPtrReturn(pThis, VERR_INVALID_HANDLE); - AssertReturn(pThis->u32Magic != RTLOCALIPCSESSION_MAGIC, VERR_INVALID_MAGIC); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_MAGIC); /* * Cancel any thread currently busy using the session, @@ -593,32 +782,437 @@ RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession) RTDECL(int) RTLocalIpcSessionRead(RTLOCALIPCSESSION hSession, void *pvBuffer, size_t cbBuffer, size_t *pcbRead) { - return VINF_SUCCESS; + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER); + /* pcbRead is optional. */ + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + /* No concurrent readers, sorry. */ + if (pThis->cRefs == 1) + { + pThis->cRefs++; + + /* + * If pcbRead is non-NULL this indicates the maximum number of bytes to read. + * If pcbRead is NULL then this is the exact number of bytes to read. + */ + size_t cbToRead = pcbRead ? *pcbRead : cbBuffer; + 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->OverlappedIO.hEvent); Assert(rc == TRUE); + DWORD cbRead = 0; + pThis->fIOPending = true; + RTCritSectLeave(&pThis->CritSect); + + if (ReadFile(pThis->hNmPipe, pvBuffer, + cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0, + &cbRead, &pThis->OverlappedIO)) + rc = VINF_SUCCESS; + else if (GetLastError() == ERROR_IO_PENDING) + { + WaitForSingleObject(pThis->OverlappedIO.hEvent, INFINITE); + if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, + &cbRead, TRUE /*fWait*/)) + rc = VINF_SUCCESS; + else + { + DWORD dwErr = GetLastError(); + AssertMsgFailed(("err=%ld\n", dwErr)); + rc = RTErrConvertFromWin32(dwErr); + } + } + else + { + DWORD dwErr = GetLastError(); + AssertMsgFailed(("err2=%ld\n", dwErr)); + rc = RTErrConvertFromWin32(dwErr); + } + + RTCritSectEnter(&pThis->CritSect); + pThis->fIOPending = false; + if (RT_FAILURE(rc)) + break; + + /* Advance. */ + cbToRead -= cbRead; + cbTotalRead += cbRead; + pvBuffer = (uint8_t *)pvBuffer + cbRead; + } + + if (pcbRead) + { + *pcbRead = cbTotalRead; + if ( RT_FAILURE(rc) + && cbTotalRead + && rc != VERR_INVALID_POINTER) + rc = VINF_SUCCESS; + } + + pThis->cRefs--; + } + else + rc = VERR_WRONG_ORDER; + RTCritSectLeave(&pThis->CritSect); + } + + return rc; +} + + +/** + * Common worker for handling I/O completion. + * + * This is used by RTLocalIpcSessionClose and RTLocalIpcSessionWrite. + * + * @returns IPRT status code. + * @param pThis The pipe instance handle. + */ +static int rtLocalIpcSessionWriteCheckCompletion(PRTLOCALIPCSESSIONINT pThis) +{ + int rc; + DWORD dwRc = WaitForSingleObject(pThis->OverlappedIO.hEvent, 0); + if (dwRc == WAIT_OBJECT_0) + { + DWORD cbWritten = 0; + if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &cbWritten, TRUE)) + { + for (;;) + { + if (cbWritten >= pThis->cbBounceBufUsed) + { + pThis->fIOPending = false; + rc = VINF_SUCCESS; + break; + } + + /* resubmit the remainder of the buffer - can this actually happen? */ + memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten); + rc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(rc == TRUE); + if (!WriteFile(pThis->hNmPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed, + &cbWritten, &pThis->OverlappedIO)) + { + DWORD 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(dwErr); + } + 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) RTLocalIpcSessionWrite(RTLOCALIPCSESSION hSession, const void *pvBuffer, size_t cbBuffer) { - return VINF_SUCCESS; + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE); + AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER); + AssertReturn(cbBuffer, VERR_INVALID_PARAMETER); + + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + /* No concurrent writers, sorry. */ + if (pThis->cRefs == 1) + { + pThis->cRefs++; + + /* + * If I/O is pending, wait for it to complete. + */ + if (pThis->fIOPending) + { + rc = rtLocalIpcSessionWriteCheckCompletion(pThis); + while (rc == VINF_TRY_AGAIN) + { + Assert(pThis->fIOPending); + HANDLE hEvent = pThis->OverlappedIO.hEvent; + RTCritSectLeave(&pThis->CritSect); + WaitForSingleObject(pThis->OverlappedIO.hEvent, INFINITE); + RTCritSectEnter(&pThis->CritSect); + } + } + if (RT_SUCCESS(rc)) + { + Assert(!pThis->fIOPending); + + /* + * Try write everything. + * No bounce buffering, cUsers protects us. + */ + size_t cbTotalWritten = 0; + while (cbBuffer > 0) + { + BOOL fRc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(fRc == TRUE); + pThis->fIOPending = true; + RTCritSectLeave(&pThis->CritSect); + + DWORD cbWritten = 0; + fRc = WriteFile(pThis->hNmPipe, pvBuffer, + cbBuffer <= ~(DWORD)0 ? (DWORD)cbBuffer : ~(DWORD)0, + &cbWritten, &pThis->OverlappedIO); + if (fRc) + { + rc = VINF_SUCCESS; + } + else + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_IO_PENDING) + { + DWORD dwRc = WaitForSingleObject(pThis->OverlappedIO.hEvent, INFINITE); + if (dwRc == WAIT_OBJECT_0) + { + if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &cbWritten, TRUE /*fWait*/)) + rc = VINF_SUCCESS; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else if (dwRc == WAIT_TIMEOUT) + rc = VERR_TIMEOUT; + else if (dwRc == WAIT_ABANDONED) + rc = VERR_INVALID_HANDLE; + else + rc = RTErrConvertFromWin32(GetLastError()); + } + else if (dwErr == ERROR_NO_DATA) + rc = VERR_BROKEN_PIPE; + else + rc = RTErrConvertFromWin32(dwErr); + } + + RTCritSectEnter(&pThis->CritSect); + pThis->fIOPending = false; + if (RT_FAILURE(rc)) + break; + + /* Advance. */ + pvBuffer = (char const *)pvBuffer + cbWritten; + cbTotalWritten += cbWritten; + cbBuffer -= cbWritten; + } + } + + pThis->cRefs--; + } + else + rc = VERR_WRONG_ORDER; + RTCritSectLeave(&pThis->CritSect); + } + + return rc; } RTDECL(int) RTLocalIpcSessionFlush(RTLOCALIPCSESSION hSession) { + /* No flushing on Windows needed since RTLocalIpcSessionWrite will block until + * all data was written (or an error occurred). */ + /** @todo Implement this as soon as we want an explicit asynchronous version of + * RTLocalIpcSessionWrite on Windows. */ return VINF_SUCCESS; } RTDECL(int) RTLocalIpcSessionWaitForData(RTLOCALIPCSESSION hSession, uint32_t cMillies) { - RTThreadSleep(1000); - return VINF_SUCCESS; + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_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->fIOPending) + hWait = pThis->OverlappedIO.hEvent; + else + { + /* Peek at the pipe buffer and see how many bytes it contains. */ + DWORD cbAvailable; + BOOL fRc = PeekNamedPipe(pThis->hNmPipe, NULL, 0, NULL, &cbAvailable, NULL); + if ( fRc + && cbAvailable) + { + rc = VINF_SUCCESS; + break; + } + else if (!fRc) + { + rc = RTErrConvertFromWin32(GetLastError()); + break; + } + + /* Start a zero byte read operation that we can wait on. */ + if (cMillies == 0) + { + rc = VERR_TIMEOUT; + break; + } + AssertBreakStmt(pThis->cRefs == 1, rc = VERR_WRONG_ORDER); + fRc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(fRc == TRUE); + DWORD cbRead = 0; + if (ReadFile(pThis->hNmPipe, pThis->abBuf, 0, &cbRead, &pThis->OverlappedIO)) + { + rc = VINF_SUCCESS; + if (iLoop > 10) + RTThreadYield(); + } + else if (GetLastError() == ERROR_IO_PENDING) + { + pThis->cRefs++; + pThis->fIOPending = true; + pThis->fZeroByteRead = true; + hWait = pThis->OverlappedIO.hEvent; + } + else + rc = RTErrConvertFromWin32(GetLastError()); + } + + 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 != RTLOCALIPCSESSION_MAGIC) + return rc; + + int rc2 = RTCritSectEnter(&pThis->CritSect); + AssertRC(rc2); + if (pThis->fZeroByteRead) + { + Assert(pThis->cRefs); + pThis->cRefs--; + pThis->fIOPending = false; + + if (rc != VINF_SUCCESS) + { + BOOL fRc = CancelIo(pThis->hNmPipe); + Assert(fRc == TRUE); + } + + DWORD cbRead = 0; + BOOL fRc = GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &cbRead, TRUE /*fWait*/); + if ( !fRc + && RT_SUCCESS(rc)) + { + DWORD dwRc = GetLastError(); + if (dwRc == ERROR_OPERATION_ABORTED) + rc = VERR_CANCELLED; + else + rc = RTErrConvertFromWin32(dwRc); + } + } + + if (RT_FAILURE(rc)) + break; + } + } + + int rc2 = RTCritSectLeave(&pThis->CritSect); + if (RT_SUCCESS(rc)) + rc = rc2; + + return rc; } RTDECL(int) RTLocalIpcSessionCancel(RTLOCALIPCSESSION hSession) { - return VINF_SUCCESS; + PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE); + + /* + * Enter the critical section, then set the cancellation flag + * and signal the event (to wake up anyone in/at WaitForSingleObject). + */ + int rc = RTCritSectEnter(&pThis->CritSect); + if (RT_SUCCESS(rc)) + { + ASMAtomicUoWriteBool(&pThis->fCancelled, true); + BOOL fRc = SetEvent(pThis->hEvent); + AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc); + + RTCritSectLeave(&pThis->CritSect); + } + + return rc; } |