VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/localipc-win.cpp@ 95084

Last change on this file since 95084 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.5 KB
Line 
1/* $Id: localipc-win.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * IPRT - Local IPC, Windows Implementation Using Named Pipes.
4 */
5
6/*
7 * Copyright (C) 2008-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_LOCALIPC
32/*
33 * We have to force NT 5.0 here because of
34 * ConvertStringSecurityDescriptorToSecurityDescriptor. Note that because of
35 * FILE_FLAG_FIRST_PIPE_INSTANCE this code actually requires W2K SP2+.
36 */
37#ifndef _WIN32_WINNT
38# define _WIN32_WINNT 0x0500 /* for ConvertStringSecurityDescriptorToSecurityDescriptor */
39#elif _WIN32_WINNT < 0x0500
40# undef _WIN32_WINNT
41# define _WIN32_WINNT 0x0500
42#endif
43#define UNICODE /* For the SDDL_ strings. */
44#include <iprt/win/windows.h>
45#include <sddl.h>
46
47#include "internal/iprt.h"
48#include <iprt/localipc.h>
49
50#include <iprt/asm.h>
51#include <iprt/assert.h>
52#include <iprt/critsect.h>
53#include <iprt/ctype.h>
54#include <iprt/err.h>
55#include <iprt/ldr.h>
56#include <iprt/log.h>
57#include <iprt/mem.h>
58#include <iprt/param.h>
59#include <iprt/string.h>
60#include <iprt/thread.h>
61#include <iprt/time.h>
62#include <iprt/utf16.h>
63
64#include "internal/magics.h"
65#include "internal-r3-win.h"
66
67
68
69/*********************************************************************************************************************************
70* Defined Constants And Macros *
71*********************************************************************************************************************************/
72/** Pipe prefix string. */
73#define RTLOCALIPC_WIN_PREFIX L"\\\\.\\pipe\\IPRT-"
74
75/** DACL for block all network access and local users other than the creator/owner.
76 *
77 * ACE format: (ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid)
78 *
79 * Note! FILE_GENERIC_WRITE (SDDL_FILE_WRITE) is evil here because it includes
80 * the FILE_CREATE_PIPE_INSTANCE(=FILE_APPEND_DATA) flag. Thus the hardcoded
81 * value 0x0012019b in the client ACE. The server-side still needs
82 * setting FILE_CREATE_PIPE_INSTANCE although.
83 * It expands to:
84 * 0x00000001 - FILE_READ_DATA
85 * 0x00000008 - FILE_READ_EA
86 * 0x00000080 - FILE_READ_ATTRIBUTES
87 * 0x00020000 - READ_CONTROL
88 * 0x00100000 - SYNCHRONIZE
89 * 0x00000002 - FILE_WRITE_DATA
90 * 0x00000010 - FILE_WRITE_EA
91 * 0x00000100 - FILE_WRITE_ATTRIBUTES
92 * = 0x0012019b (client)
93 * + (only for server):
94 * 0x00000004 - FILE_CREATE_PIPE_INSTANCE
95 * = 0x0012019f
96 *
97 * @todo Triple check this!
98 * @todo EVERYONE -> AUTHENTICATED USERS or something more appropriate?
99 * @todo Have trouble allowing the owner FILE_CREATE_PIPE_INSTANCE access, so for now I'm hacking
100 * it just to get progress - the service runs as local system.
101 * The CREATOR OWNER and PERSONAL SELF works (the former is only involved in inheriting
102 * it seems, which is why it won't work. The latter I've no idea about. Perhaps the solution
103 * is to go the annoying route of OpenProcessToken, QueryTokenInformation,
104 * ConvertSidToStringSid and then use the result... Suggestions are very welcome
105 */
106#define RTLOCALIPC_WIN_SDDL_BASE \
107 SDDL_DACL SDDL_DELIMINATOR \
108 SDDL_ACE_BEGIN SDDL_ACCESS_DENIED L";;" SDDL_GENERIC_ALL L";;;" SDDL_NETWORK SDDL_ACE_END \
109 SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" SDDL_FILE_ALL L";;;" SDDL_LOCAL_SYSTEM SDDL_ACE_END
110
111#define RTLOCALIPC_WIN_SDDL_SERVER \
112 RTLOCALIPC_WIN_SDDL_BASE \
113 SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" L"0x0012019f" L";;;" SDDL_EVERYONE SDDL_ACE_END
114
115#define RTLOCALIPC_WIN_SDDL_CLIENT \
116 RTLOCALIPC_WIN_SDDL_BASE \
117 SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" L"0x0012019b" L";;;" SDDL_EVERYONE SDDL_ACE_END
118
119// SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" SDDL_GENERIC_ALL L";;;" SDDL_PERSONAL_SELF SDDL_ACE_END \
120// SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";CIOI;" SDDL_GENERIC_ALL L";;;" SDDL_CREATOR_OWNER SDDL_ACE_END
121// SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" L"0x0012019b" L";;;" SDDL_EVERYONE SDDL_ACE_END
122// SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" SDDL_FILE_ALL L";;;" SDDL_LOCAL_SYSTEM SDDL_ACE_END
123
124
125/*********************************************************************************************************************************
126* Structures and Typedefs *
127*********************************************************************************************************************************/
128/**
129 * Local IPC service instance, Windows.
130 */
131typedef struct RTLOCALIPCSERVERINT
132{
133 /** The magic (RTLOCALIPCSERVER_MAGIC). */
134 uint32_t u32Magic;
135 /** The creation flags. */
136 uint32_t fFlags;
137 /** Critical section protecting the structure. */
138 RTCRITSECT CritSect;
139 /** The number of references to the instance.
140 * @remarks The reference counting isn't race proof. */
141 uint32_t volatile cRefs;
142 /** Indicates that there is a pending cancel request. */
143 bool volatile fCancelled;
144 /** The named pipe handle. */
145 HANDLE hNmPipe;
146 /** The handle to the event object we're using for overlapped I/O. */
147 HANDLE hEvent;
148 /** The overlapped I/O structure. */
149 OVERLAPPED OverlappedIO;
150 /** The full pipe name (variable length). */
151 RTUTF16 wszName[1];
152} RTLOCALIPCSERVERINT;
153/** Pointer to a local IPC server instance (Windows). */
154typedef RTLOCALIPCSERVERINT *PRTLOCALIPCSERVERINT;
155
156
157/**
158 * Local IPC session instance, Windows.
159 *
160 * This is a named pipe and we should probably merge the pipe code with this to
161 * save work and code duplication.
162 */
163typedef struct RTLOCALIPCSESSIONINT
164{
165 /** The magic (RTLOCALIPCSESSION_MAGIC). */
166 uint32_t u32Magic;
167 /** Critical section protecting the structure. */
168 RTCRITSECT CritSect;
169 /** The number of references to the instance.
170 * @remarks The reference counting isn't race proof. */
171 uint32_t volatile cRefs;
172 /** Set if the zero byte read that the poll code using is pending. */
173 bool fZeroByteRead;
174 /** Indicates that there is a pending cancel request. */
175 bool volatile fCancelled;
176 /** Set if this is the server side, clear if the client. */
177 bool fServerSide;
178 /** The named pipe handle. */
179 HANDLE hNmPipe;
180 struct
181 {
182 RTTHREAD hActiveThread;
183 /** The handle to the event object we're using for overlapped I/O. */
184 HANDLE hEvent;
185 /** The overlapped I/O structure. */
186 OVERLAPPED OverlappedIO;
187 }
188 /** Overlapped reads. */
189 Read,
190 /** Overlapped writes. */
191 Write;
192#if 0 /* Non-blocking writes are not yet supported. */
193 /** Bounce buffer for writes. */
194 uint8_t *pbBounceBuf;
195 /** Amount of used buffer space. */
196 size_t cbBounceBufUsed;
197 /** Amount of allocated buffer space. */
198 size_t cbBounceBufAlloc;
199#endif
200 /** Buffer for the zero byte read.
201 * Used in RTLocalIpcSessionWaitForData(). */
202 uint8_t abBuf[8];
203} RTLOCALIPCSESSIONINT;
204/** Pointer to a local IPC session instance (Windows). */
205typedef RTLOCALIPCSESSIONINT *PRTLOCALIPCSESSIONINT;
206
207
208/*********************************************************************************************************************************
209* Internal Functions *
210*********************************************************************************************************************************/
211static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSIONINT *ppSession, HANDLE hNmPipeSession);
212
213
214/*********************************************************************************************************************************
215* Global Variables *
216*********************************************************************************************************************************/
217static bool volatile g_fResolvedApis = false;
218/** advapi32.dll API ConvertStringSecurityDescriptorToSecurityDescriptorW. */
219static decltype(ConvertStringSecurityDescriptorToSecurityDescriptorW) *g_pfnSSDLToSecDescW = NULL;
220
221
222/**
223 * Builds and allocates the security descriptor required for securing the local pipe.
224 *
225 * @return IPRT status code.
226 * @param ppDesc Where to store the allocated security descriptor on success.
227 * Must be free'd using LocalFree().
228 * @param fServer Whether it's for a server or client instance.
229 */
230static int rtLocalIpcServerWinAllocSecurityDescriptior(PSECURITY_DESCRIPTOR *ppDesc, bool fServer)
231{
232 /*
233 * Resolve the API the first time around.
234 */
235 if (!g_fResolvedApis)
236 {
237 g_pfnSSDLToSecDescW = (decltype(g_pfnSSDLToSecDescW))RTLdrGetSystemSymbol("advapi32.dll", "ConvertStringSecurityDescriptorToSecurityDescriptorW");
238 ASMCompilerBarrier();
239 g_fResolvedApis = true;
240 }
241
242 int rc;
243 PSECURITY_DESCRIPTOR pSecDesc = NULL;
244 if (g_pfnSSDLToSecDescW)
245 {
246 /*
247 * We'll create a security descriptor from a SDDL that denies
248 * access to network clients (this is local IPC after all), it
249 * makes some further restrictions to prevent non-authenticated
250 * users from screwing around.
251 */
252 PCRTUTF16 pwszSDDL = fServer ? RTLOCALIPC_WIN_SDDL_SERVER : RTLOCALIPC_WIN_SDDL_CLIENT;
253 if (g_pfnSSDLToSecDescW(pwszSDDL, SDDL_REVISION_1, &pSecDesc, NULL))
254 {
255 AssertPtr(pSecDesc);
256 *ppDesc = pSecDesc;
257 return VINF_SUCCESS;
258 }
259
260 rc = RTErrConvertFromWin32(GetLastError());
261 }
262 else
263 {
264 /* Windows OSes < W2K SP2 not supported for now, bail out. */
265 /** @todo Implement me! */
266 rc = VERR_NOT_SUPPORTED;
267 }
268 return rc;
269}
270
271
272/**
273 * Creates a named pipe instance.
274 *
275 * This is used by both RTLocalIpcServerCreate and RTLocalIpcServerListen.
276 *
277 * @return IPRT status code.
278 * @param phNmPipe Where to store the named pipe handle on success.
279 * This will be set to INVALID_HANDLE_VALUE on failure.
280 * @param pwszPipeName The named pipe name, full, UTF-16 encoded.
281 * @param fFirst Set on the first call (from RTLocalIpcServerCreate),
282 * otherwise clear. Governs the
283 * FILE_FLAG_FIRST_PIPE_INSTANCE flag.
284 */
285static int rtLocalIpcServerWinCreatePipeInstance(PHANDLE phNmPipe, PCRTUTF16 pwszPipeName, bool fFirst)
286{
287 *phNmPipe = INVALID_HANDLE_VALUE;
288
289 PSECURITY_DESCRIPTOR pSecDesc;
290 int rc = rtLocalIpcServerWinAllocSecurityDescriptior(&pSecDesc, fFirst /* Server? */);
291 if (RT_SUCCESS(rc))
292 {
293 SECURITY_ATTRIBUTES SecAttrs;
294 SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
295 SecAttrs.lpSecurityDescriptor = pSecDesc;
296 SecAttrs.bInheritHandle = FALSE;
297
298 DWORD fOpenMode = PIPE_ACCESS_DUPLEX
299 | PIPE_WAIT
300 | FILE_FLAG_OVERLAPPED;
301 if ( fFirst
302 && ( g_enmWinVer >= kRTWinOSType_XP
303 || ( g_enmWinVer == kRTWinOSType_2K
304 && g_WinOsInfoEx.wServicePackMajor >= 2) ) )
305 fOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE; /* Introduced with W2K SP2 */
306
307 HANDLE hNmPipe = CreateNamedPipeW(pwszPipeName, /* lpName */
308 fOpenMode, /* dwOpenMode */
309 PIPE_TYPE_BYTE, /* dwPipeMode */
310 PIPE_UNLIMITED_INSTANCES, /* nMaxInstances */
311 PAGE_SIZE, /* nOutBufferSize (advisory) */
312 PAGE_SIZE, /* nInBufferSize (ditto) */
313 30*1000, /* nDefaultTimeOut = 30 sec */
314 &SecAttrs); /* lpSecurityAttributes */
315 LocalFree(pSecDesc);
316 if (hNmPipe != INVALID_HANDLE_VALUE)
317 *phNmPipe = hNmPipe;
318 else
319 rc = RTErrConvertFromWin32(GetLastError());
320 }
321
322 return rc;
323}
324
325
326/**
327 * Validates the user specified name.
328 *
329 * @returns IPRT status code.
330 * @param pszName The name to validate.
331 * @param pcwcFullName Where to return the UTF-16 length of the full name.
332 * @param fNative Whether it's a native name or a portable name.
333 */
334static int rtLocalIpcWinValidateName(const char *pszName, size_t *pcwcFullName, bool fNative)
335{
336 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
337 AssertReturn(*pszName, VERR_INVALID_NAME);
338
339 if (!fNative)
340 {
341 size_t cwcName = RT_ELEMENTS(RTLOCALIPC_WIN_PREFIX) - 1;
342 for (;;)
343 {
344 char ch = *pszName++;
345 if (!ch)
346 break;
347 AssertReturn(!RT_C_IS_CNTRL(ch), VERR_INVALID_NAME);
348 AssertReturn((unsigned)ch < 0x80, VERR_INVALID_NAME);
349 AssertReturn(ch != '\\', VERR_INVALID_NAME);
350 AssertReturn(ch != '/', VERR_INVALID_NAME);
351 cwcName++;
352 }
353 *pcwcFullName = cwcName;
354 }
355 else
356 {
357 int rc = RTStrCalcUtf16LenEx(pszName, RTSTR_MAX, pcwcFullName);
358 AssertRCReturn(rc, rc);
359 }
360
361 return VINF_SUCCESS;
362}
363
364
365/**
366 * Constructs the full pipe name as UTF-16.
367 *
368 * @returns IPRT status code.
369 * @param pszName The user supplied name. ASSUMES reasonable length
370 * for now, so no long path prefixing needed.
371 * @param pwszFullName The output buffer.
372 * @param cwcFullName The output buffer size excluding the terminator.
373 * @param fNative Whether the user supplied name is a native or
374 * portable one.
375 */
376static int rtLocalIpcWinConstructName(const char *pszName, PRTUTF16 pwszFullName, size_t cwcFullName, bool fNative)
377{
378 if (!fNative)
379 {
380 static RTUTF16 const s_wszPrefix[] = RTLOCALIPC_WIN_PREFIX;
381 Assert(cwcFullName * sizeof(RTUTF16) > sizeof(s_wszPrefix));
382 memcpy(pwszFullName, s_wszPrefix, sizeof(s_wszPrefix));
383 cwcFullName -= RT_ELEMENTS(s_wszPrefix) - 1;
384 pwszFullName += RT_ELEMENTS(s_wszPrefix) - 1;
385 }
386 return RTStrToUtf16Ex(pszName, RTSTR_MAX, &pwszFullName, cwcFullName + 1, NULL);
387}
388
389
390RTDECL(int) RTLocalIpcServerCreate(PRTLOCALIPCSERVER phServer, const char *pszName, uint32_t fFlags)
391{
392 /*
393 * Validate parameters.
394 */
395 AssertPtrReturn(phServer, VERR_INVALID_POINTER);
396 *phServer = NIL_RTLOCALIPCSERVER;
397 AssertReturn(!(fFlags & ~RTLOCALIPC_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
398 size_t cwcFullName;
399 int rc = rtLocalIpcWinValidateName(pszName, &cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
400 if (RT_SUCCESS(rc))
401 {
402 /*
403 * Allocate and initialize the instance data.
404 */
405 size_t cbThis = RT_UOFFSETOF_DYN(RTLOCALIPCSERVERINT, wszName[cwcFullName + 1]);
406 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)RTMemAllocVar(cbThis);
407 AssertReturn(pThis, VERR_NO_MEMORY);
408
409 pThis->u32Magic = RTLOCALIPCSERVER_MAGIC;
410 pThis->cRefs = 1; /* the one we return */
411 pThis->fCancelled = false;
412
413 rc = rtLocalIpcWinConstructName(pszName, pThis->wszName, cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
414 if (RT_SUCCESS(rc))
415 {
416 rc = RTCritSectInit(&pThis->CritSect);
417 if (RT_SUCCESS(rc))
418 {
419 pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
420 FALSE /*bInitialState*/, NULL /*lpName*/);
421 if (pThis->hEvent != NULL)
422 {
423 RT_ZERO(pThis->OverlappedIO);
424 pThis->OverlappedIO.Internal = STATUS_PENDING;
425 pThis->OverlappedIO.hEvent = pThis->hEvent;
426
427 rc = rtLocalIpcServerWinCreatePipeInstance(&pThis->hNmPipe, pThis->wszName, true /* fFirst */);
428 if (RT_SUCCESS(rc))
429 {
430 *phServer = pThis;
431 return VINF_SUCCESS;
432 }
433
434 BOOL fRc = CloseHandle(pThis->hEvent);
435 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
436 }
437 else
438 rc = RTErrConvertFromWin32(GetLastError());
439
440 int rc2 = RTCritSectDelete(&pThis->CritSect);
441 AssertRC(rc2);
442 }
443 }
444 RTMemFree(pThis);
445 }
446 return rc;
447}
448
449
450/**
451 * Retains a reference to the server instance.
452 *
453 * @returns
454 * @param pThis The server instance.
455 */
456DECLINLINE(void) rtLocalIpcServerRetain(PRTLOCALIPCSERVERINT pThis)
457{
458 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
459 Assert(cRefs < UINT32_MAX / 2 && cRefs); NOREF(cRefs);
460}
461
462
463/**
464 * Call when the reference count reaches 0.
465 *
466 * Caller owns the critsect.
467 *
468 * @returns VINF_OBJECT_DESTROYED
469 * @param pThis The instance to destroy.
470 */
471DECL_NO_INLINE(static, int) rtLocalIpcServerWinDestroy(PRTLOCALIPCSERVERINT pThis)
472{
473 Assert(pThis->u32Magic == ~RTLOCALIPCSERVER_MAGIC);
474 pThis->u32Magic = ~RTLOCALIPCSERVER_MAGIC;
475
476 BOOL fRc = CloseHandle(pThis->hNmPipe);
477 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
478 pThis->hNmPipe = INVALID_HANDLE_VALUE;
479
480 fRc = CloseHandle(pThis->hEvent);
481 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
482 pThis->hEvent = NULL;
483
484 RTCritSectLeave(&pThis->CritSect);
485 RTCritSectDelete(&pThis->CritSect);
486
487 RTMemFree(pThis);
488 return VINF_OBJECT_DESTROYED;
489}
490
491
492/**
493 * Server instance destructor.
494 *
495 * @returns VINF_OBJECT_DESTROYED
496 * @param pThis The server instance.
497 */
498DECL_NO_INLINE(static, int) rtLocalIpcServerDtor(PRTLOCALIPCSERVERINT pThis)
499{
500 RTCritSectEnter(&pThis->CritSect);
501 return rtLocalIpcServerWinDestroy(pThis);
502}
503
504
505/**
506 * Releases a reference to the server instance.
507 *
508 * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed.
509 * @param pThis The server instance.
510 */
511DECLINLINE(int) rtLocalIpcServerRelease(PRTLOCALIPCSERVERINT pThis)
512{
513 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
514 Assert(cRefs < UINT32_MAX / 2);
515 if (!cRefs)
516 return rtLocalIpcServerDtor(pThis);
517 return VINF_SUCCESS;
518}
519
520
521/**
522 * Releases a reference to the server instance and leaves the critsect.
523 *
524 * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed.
525 * @param pThis The server instance.
526 */
527DECLINLINE(int) rtLocalIpcServerReleaseAndUnlock(PRTLOCALIPCSERVERINT pThis)
528{
529 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
530 Assert(cRefs < UINT32_MAX / 2);
531 if (!cRefs)
532 return rtLocalIpcServerWinDestroy(pThis);
533 return RTCritSectLeave(&pThis->CritSect);
534}
535
536
537
538RTDECL(int) RTLocalIpcServerDestroy(RTLOCALIPCSERVER hServer)
539{
540 /*
541 * Validate input.
542 */
543 if (hServer == NIL_RTLOCALIPCSERVER)
544 return VINF_SUCCESS;
545 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
546 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
547 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
548
549 /*
550 * Cancel any thread currently busy using the server,
551 * leaving the cleanup to it.
552 */
553 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTLOCALIPCSERVER_MAGIC, RTLOCALIPCSERVER_MAGIC), VERR_WRONG_ORDER);
554
555 RTCritSectEnter(&pThis->CritSect);
556
557 /* Cancel everything. */
558 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
559 if (pThis->cRefs > 1)
560 {
561 BOOL fRc = SetEvent(pThis->hEvent);
562 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
563 }
564
565 return rtLocalIpcServerReleaseAndUnlock(pThis);
566}
567
568
569RTDECL(int) RTLocalIpcServerGrantGroupAccess(RTLOCALIPCSERVER hServer, RTGID gid)
570{
571 RT_NOREF_PV(hServer); RT_NOREF(gid);
572 return VERR_NOT_SUPPORTED;
573}
574
575
576RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION phClientSession)
577{
578 /*
579 * Validate input.
580 */
581 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
582 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
583 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
584 AssertPtrReturn(phClientSession, VERR_INVALID_POINTER);
585
586 /*
587 * Enter the critsect before inspecting the object further.
588 */
589 int rc = RTCritSectEnter(&pThis->CritSect);
590 AssertRCReturn(rc, rc);
591
592 rtLocalIpcServerRetain(pThis);
593 if (!pThis->fCancelled)
594 {
595 ResetEvent(pThis->hEvent);
596
597 RTCritSectLeave(&pThis->CritSect);
598
599 /*
600 * Try connect a client. We need to use overlapped I/O here because
601 * of the cancellation done by RTLocalIpcServerCancel and RTLocalIpcServerDestroy.
602 */
603 SetLastError(NO_ERROR);
604 BOOL fRc = ConnectNamedPipe(pThis->hNmPipe, &pThis->OverlappedIO);
605 DWORD dwErr = fRc ? NO_ERROR : GetLastError();
606 if ( !fRc
607 && dwErr == ERROR_IO_PENDING)
608 {
609 WaitForSingleObject(pThis->hEvent, INFINITE);
610 DWORD dwIgnored;
611 fRc = GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &dwIgnored, FALSE /* bWait*/);
612 dwErr = fRc ? NO_ERROR : GetLastError();
613 }
614
615 RTCritSectEnter(&pThis->CritSect);
616 if ( !pThis->fCancelled /* Event signalled but not cancelled? */
617 && pThis->u32Magic == RTLOCALIPCSERVER_MAGIC)
618 {
619 /*
620 * Still alive, some error or an actual client.
621 *
622 * If it's the latter we'll have to create a new pipe instance that
623 * replaces the current one for the server. The current pipe instance
624 * will be assigned to the client session.
625 */
626 if ( fRc
627 || dwErr == ERROR_PIPE_CONNECTED)
628 {
629 HANDLE hNmPipe;
630 rc = rtLocalIpcServerWinCreatePipeInstance(&hNmPipe, pThis->wszName, false /* fFirst */);
631 if (RT_SUCCESS(rc))
632 {
633 HANDLE hNmPipeSession = pThis->hNmPipe; /* consumed */
634 pThis->hNmPipe = hNmPipe;
635 rc = rtLocalIpcWinCreateSession(phClientSession, hNmPipeSession);
636 }
637 else
638 {
639 /*
640 * We failed to create a new instance for the server, disconnect
641 * the client and fail. Don't try service the client here.
642 */
643 fRc = DisconnectNamedPipe(pThis->hNmPipe);
644 AssertMsg(fRc, ("%d\n", GetLastError()));
645 }
646 }
647 else
648 rc = RTErrConvertFromWin32(dwErr);
649 }
650 else
651 {
652 /*
653 * Cancelled.
654 *
655 * Cancel the overlapped io if it didn't complete (must be done
656 * in the this thread) or disconnect the client.
657 */
658 Assert(pThis->fCancelled);
659 if ( fRc
660 || dwErr == ERROR_PIPE_CONNECTED)
661 fRc = DisconnectNamedPipe(pThis->hNmPipe);
662 else if (dwErr == ERROR_IO_PENDING)
663 fRc = CancelIo(pThis->hNmPipe);
664 else
665 fRc = TRUE;
666 AssertMsg(fRc, ("%d\n", GetLastError()));
667 rc = VERR_CANCELLED;
668 }
669 }
670 else
671 {
672 /*pThis->fCancelled = false; - Terrible interface idea. Add API to clear fCancelled if ever required. */
673 rc = VERR_CANCELLED;
674 }
675 rtLocalIpcServerReleaseAndUnlock(pThis);
676 return rc;
677}
678
679
680RTDECL(int) RTLocalIpcServerCancel(RTLOCALIPCSERVER hServer)
681{
682 /*
683 * Validate input.
684 */
685 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
686 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
687 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
688
689 /*
690 * Enter the critical section, then set the cancellation flag
691 * and signal the event (to wake up anyone in/at WaitForSingleObject).
692 */
693 rtLocalIpcServerRetain(pThis);
694 int rc = RTCritSectEnter(&pThis->CritSect);
695 if (RT_SUCCESS(rc))
696 {
697 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
698
699 BOOL fRc = SetEvent(pThis->hEvent);
700 if (fRc)
701 rc = VINF_SUCCESS;
702 else
703 {
704 DWORD dwErr = GetLastError();
705 AssertMsgFailed(("dwErr=%u\n", dwErr));
706 rc = RTErrConvertFromWin32(dwErr);
707 }
708
709 rtLocalIpcServerReleaseAndUnlock(pThis);
710 }
711 else
712 rtLocalIpcServerRelease(pThis);
713 return rc;
714}
715
716
717/**
718 * Create a session instance for a new server client or a client connect.
719 *
720 * @returns IPRT status code.
721 *
722 * @param ppSession Where to store the session handle on success.
723 * @param hNmPipeSession The named pipe handle if server calling,
724 * INVALID_HANDLE_VALUE if client connect. This will
725 * be consumed by this session, meaning on failure to
726 * create the session it will be closed.
727 */
728static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSIONINT *ppSession, HANDLE hNmPipeSession)
729{
730 AssertPtr(ppSession);
731
732 /*
733 * Allocate and initialize the session instance data.
734 */
735 int rc;
736 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAllocZ(sizeof(*pThis));
737 if (pThis)
738 {
739 pThis->u32Magic = RTLOCALIPCSESSION_MAGIC;
740 pThis->cRefs = 1; /* our ref */
741 pThis->fCancelled = false;
742 pThis->fZeroByteRead = false;
743 pThis->fServerSide = hNmPipeSession != INVALID_HANDLE_VALUE;
744 pThis->hNmPipe = hNmPipeSession;
745#if 0 /* Non-blocking writes are not yet supported. */
746 pThis->pbBounceBuf = NULL;
747 pThis->cbBounceBufAlloc = 0;
748 pThis->cbBounceBufUsed = 0;
749#endif
750 rc = RTCritSectInit(&pThis->CritSect);
751 if (RT_SUCCESS(rc))
752 {
753 pThis->Read.hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
754 FALSE /*bInitialState*/, NULL /*lpName*/);
755 if (pThis->Read.hEvent != NULL)
756 {
757 pThis->Read.OverlappedIO.Internal = STATUS_PENDING;
758 pThis->Read.OverlappedIO.hEvent = pThis->Read.hEvent;
759 pThis->Read.hActiveThread = NIL_RTTHREAD;
760
761 pThis->Write.hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
762 FALSE /*bInitialState*/, NULL /*lpName*/);
763 if (pThis->Write.hEvent != NULL)
764 {
765 pThis->Write.OverlappedIO.Internal = STATUS_PENDING;
766 pThis->Write.OverlappedIO.hEvent = pThis->Write.hEvent;
767 pThis->Write.hActiveThread = NIL_RTTHREAD;
768
769 *ppSession = pThis;
770 return VINF_SUCCESS;
771 }
772
773 CloseHandle(pThis->Read.hEvent);
774 }
775
776 /* bail out */
777 rc = RTErrConvertFromWin32(GetLastError());
778 RTCritSectDelete(&pThis->CritSect);
779 }
780 RTMemFree(pThis);
781 }
782 else
783 rc = VERR_NO_MEMORY;
784
785 if (hNmPipeSession != INVALID_HANDLE_VALUE)
786 {
787 BOOL fRc = CloseHandle(hNmPipeSession);
788 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
789 }
790 return rc;
791}
792
793
794RTDECL(int) RTLocalIpcSessionConnect(PRTLOCALIPCSESSION phSession, const char *pszName, uint32_t fFlags)
795{
796 /*
797 * Validate input.
798 */
799 AssertPtrReturn(phSession, VERR_INVALID_POINTER);
800 AssertReturn(!(fFlags & ~RTLOCALIPC_C_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
801
802 size_t cwcFullName;
803 int rc = rtLocalIpcWinValidateName(pszName, &cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
804 if (RT_SUCCESS(rc))
805 {
806 /*
807 * Create a session (shared with server client session creation).
808 */
809 PRTLOCALIPCSESSIONINT pThis;
810 rc = rtLocalIpcWinCreateSession(&pThis, INVALID_HANDLE_VALUE);
811 if (RT_SUCCESS(rc))
812 {
813 /*
814 * Try open the pipe.
815 */
816 PSECURITY_DESCRIPTOR pSecDesc;
817 rc = rtLocalIpcServerWinAllocSecurityDescriptior(&pSecDesc, false /*fServer*/);
818 if (RT_SUCCESS(rc))
819 {
820 PRTUTF16 pwszFullName = RTUtf16Alloc((cwcFullName + 1) * sizeof(RTUTF16));
821 if (pwszFullName)
822 rc = rtLocalIpcWinConstructName(pszName, pwszFullName, cwcFullName,
823 RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
824 else
825 rc = VERR_NO_UTF16_MEMORY;
826 if (RT_SUCCESS(rc))
827 {
828 SECURITY_ATTRIBUTES SecAttrs;
829 SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
830 SecAttrs.lpSecurityDescriptor = pSecDesc;
831 SecAttrs.bInheritHandle = FALSE;
832
833 /* The SECURITY_XXX flags are needed in order to prevent the server from impersonating with
834 this thread's security context (supported at least back to NT 3.51). See @bugref{9773}. */
835 HANDLE hPipe = CreateFileW(pwszFullName,
836 GENERIC_READ | GENERIC_WRITE,
837 0 /*no sharing*/,
838 &SecAttrs,
839 OPEN_EXISTING,
840 FILE_FLAG_OVERLAPPED | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS,
841 NULL /*no template handle*/);
842 if (hPipe != INVALID_HANDLE_VALUE)
843 {
844 pThis->hNmPipe = hPipe;
845
846 LocalFree(pSecDesc);
847 RTUtf16Free(pwszFullName);
848
849 /*
850 * We're done!
851 */
852 *phSession = pThis;
853 return VINF_SUCCESS;
854 }
855
856 rc = RTErrConvertFromWin32(GetLastError());
857 }
858
859 RTUtf16Free(pwszFullName);
860 LocalFree(pSecDesc);
861 }
862
863 /* destroy the session handle. */
864 CloseHandle(pThis->Read.hEvent);
865 CloseHandle(pThis->Write.hEvent);
866 RTCritSectDelete(&pThis->CritSect);
867
868 RTMemFree(pThis);
869 }
870 }
871 return rc;
872}
873
874
875/**
876 * Cancells all pending I/O operations, forcing the methods to return with
877 * VERR_CANCELLED (unless they've got actual data to return).
878 *
879 * Used by RTLocalIpcSessionCancel and RTLocalIpcSessionClose.
880 *
881 * @returns IPRT status code.
882 * @param pThis The client session instance.
883 */
884static int rtLocalIpcWinCancel(PRTLOCALIPCSESSIONINT pThis)
885{
886 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
887
888 /*
889 * Call CancelIo since this call cancels both read and write oriented operations.
890 */
891 if ( pThis->fZeroByteRead
892 || pThis->Read.hActiveThread != NIL_RTTHREAD
893 || pThis->Write.hActiveThread != NIL_RTTHREAD)
894 CancelIo(pThis->hNmPipe);
895
896 /*
897 * Set both event semaphores.
898 */
899 BOOL fRc = SetEvent(pThis->Read.hEvent);
900 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
901 fRc = SetEvent(pThis->Write.hEvent);
902 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
903
904 return VINF_SUCCESS;
905}
906
907
908/**
909 * Retains a reference to the session instance.
910 *
911 * @param pThis The client session instance.
912 */
913DECLINLINE(void) rtLocalIpcSessionRetain(PRTLOCALIPCSESSIONINT pThis)
914{
915 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
916 Assert(cRefs < UINT32_MAX / 2 && cRefs); NOREF(cRefs);
917}
918
919
920RTDECL(uint32_t) RTLocalIpcSessionRetain(RTLOCALIPCSESSION hSession)
921{
922 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
923 AssertPtrReturn(pThis, UINT32_MAX);
924 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
925
926 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
927 Assert(cRefs < UINT32_MAX / 2 && cRefs);
928 return cRefs;
929}
930
931
932/**
933 * Call when the reference count reaches 0.
934 *
935 * Caller owns the critsect.
936 *
937 * @returns VINF_OBJECT_DESTROYED
938 * @param pThis The instance to destroy.
939 */
940DECL_NO_INLINE(static, int) rtLocalIpcSessionWinDestroy(PRTLOCALIPCSESSIONINT pThis)
941{
942 BOOL fRc = CloseHandle(pThis->hNmPipe);
943 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
944 pThis->hNmPipe = INVALID_HANDLE_VALUE;
945
946 fRc = CloseHandle(pThis->Write.hEvent);
947 AssertMsg(fRc, ("%d\n", GetLastError()));
948 pThis->Write.hEvent = NULL;
949
950 fRc = CloseHandle(pThis->Read.hEvent);
951 AssertMsg(fRc, ("%d\n", GetLastError()));
952 pThis->Read.hEvent = NULL;
953
954 int rc2 = RTCritSectLeave(&pThis->CritSect); AssertRC(rc2);
955 RTCritSectDelete(&pThis->CritSect);
956
957 RTMemFree(pThis);
958 return VINF_OBJECT_DESTROYED;
959}
960
961
962/**
963 * Releases a reference to the session instance and unlock it.
964 *
965 * @returns VINF_SUCCESS or VINF_OBJECT_DESTROYED as appropriate.
966 * @param pThis The session instance.
967 */
968DECLINLINE(int) rtLocalIpcSessionReleaseAndUnlock(PRTLOCALIPCSESSIONINT pThis)
969{
970 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
971 Assert(cRefs < UINT32_MAX / 2);
972 if (!cRefs)
973 return rtLocalIpcSessionWinDestroy(pThis);
974
975 int rc2 = RTCritSectLeave(&pThis->CritSect); AssertRC(rc2);
976 Log(("rtLocalIpcSessionReleaseAndUnlock: %u refs left\n", cRefs));
977 return VINF_SUCCESS;
978}
979
980
981RTDECL(uint32_t) RTLocalIpcSessionRelease(RTLOCALIPCSESSION hSession)
982{
983 if (hSession == NIL_RTLOCALIPCSESSION)
984 return 0;
985
986 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
987 AssertPtrReturn(pThis, UINT32_MAX);
988 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
989
990 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
991 Assert(cRefs < UINT32_MAX / 2);
992 if (cRefs)
993 Log(("RTLocalIpcSessionRelease: %u refs left\n", cRefs));
994 else
995 {
996 RTCritSectEnter(&pThis->CritSect);
997 rtLocalIpcSessionWinDestroy(pThis);
998 }
999 return cRefs;
1000}
1001
1002
1003RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession)
1004{
1005 /*
1006 * Validate input.
1007 */
1008 if (hSession == NIL_RTLOCALIPCSESSION)
1009 return VINF_SUCCESS;
1010 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1011 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1012 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1013
1014 /*
1015 * Invalidate the instance, cancel all outstanding I/O and drop our reference.
1016 */
1017 RTCritSectEnter(&pThis->CritSect);
1018 rtLocalIpcWinCancel(pThis);
1019 return rtLocalIpcSessionReleaseAndUnlock(pThis);
1020}
1021
1022
1023/**
1024 * Handles WaitForSingleObject return value when waiting for a zero byte read.
1025 *
1026 * The zero byte read is started by the RTLocalIpcSessionWaitForData method and
1027 * left pending when the function times out. This saves us the problem of
1028 * CancelIo messing with all active I/O operations and the trouble of restarting
1029 * the zero byte read the next time the method is called. However should
1030 * RTLocalIpcSessionRead be called after a failed RTLocalIpcSessionWaitForData
1031 * call, the zero byte read will still be pending and it must wait for it to
1032 * complete before the OVERLAPPEDIO structure can be reused.
1033 *
1034 * Thus, both functions will do WaitForSingleObject and share this routine to
1035 * handle the outcome.
1036 *
1037 * @returns IPRT status code.
1038 * @param pThis The session instance.
1039 * @param rcWait The WaitForSingleObject return code.
1040 */
1041static int rtLocalIpcWinGetZeroReadResult(PRTLOCALIPCSESSIONINT pThis, DWORD rcWait)
1042{
1043 int rc;
1044 DWORD cbRead = 42;
1045 if (rcWait == WAIT_OBJECT_0)
1046 {
1047 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, !pThis->fCancelled /*fWait*/))
1048 {
1049 Assert(cbRead == 0);
1050 rc = VINF_SUCCESS;
1051 pThis->fZeroByteRead = false;
1052 }
1053 else if (pThis->fCancelled)
1054 rc = VERR_CANCELLED;
1055 else
1056 rc = RTErrConvertFromWin32(GetLastError());
1057 }
1058 else
1059 {
1060 /* We try get the result here too, just in case we're lucky, but no waiting. */
1061 DWORD dwErr = GetLastError();
1062 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, FALSE /*fWait*/))
1063 {
1064 Assert(cbRead == 0);
1065 rc = VINF_SUCCESS;
1066 pThis->fZeroByteRead = false;
1067 }
1068 else if (rcWait == WAIT_TIMEOUT)
1069 rc = VERR_TIMEOUT;
1070 else if (rcWait == WAIT_ABANDONED)
1071 rc = VERR_INVALID_HANDLE;
1072 else
1073 rc = RTErrConvertFromWin32(dwErr);
1074 }
1075 return rc;
1076}
1077
1078
1079RTDECL(int) RTLocalIpcSessionRead(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
1080{
1081 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1082 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1083 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1084 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1085 /* pcbRead is optional. */
1086
1087 int rc = RTCritSectEnter(&pThis->CritSect);
1088 if (RT_SUCCESS(rc))
1089 {
1090 rtLocalIpcSessionRetain(pThis);
1091 if (pThis->Read.hActiveThread == NIL_RTTHREAD)
1092 {
1093 pThis->Read.hActiveThread = RTThreadSelf();
1094
1095 size_t cbTotalRead = 0;
1096 while (cbToRead > 0)
1097 {
1098 DWORD cbRead = 0;
1099 if (!pThis->fCancelled)
1100 {
1101 /*
1102 * Wait for pending zero byte read, if necessary.
1103 * Note! It cannot easily be cancelled due to concurrent current writes.
1104 */
1105 if (!pThis->fZeroByteRead)
1106 { /* likely */ }
1107 else
1108 {
1109 RTCritSectLeave(&pThis->CritSect);
1110 DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, RT_MS_1MIN);
1111 RTCritSectEnter(&pThis->CritSect);
1112
1113 rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
1114 if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT)
1115 continue;
1116 break;
1117 }
1118
1119 /*
1120 * Kick of a an overlapped read. It should return immediately if
1121 * there is bytes in the buffer. If not, we'll cancel it and see
1122 * what we get back.
1123 */
1124 rc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(rc == TRUE);
1125 RTCritSectLeave(&pThis->CritSect);
1126
1127 if (ReadFile(pThis->hNmPipe, pvBuf,
1128 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
1129 &cbRead, &pThis->Read.OverlappedIO))
1130 {
1131 RTCritSectEnter(&pThis->CritSect);
1132 rc = VINF_SUCCESS;
1133 }
1134 else if (GetLastError() == ERROR_IO_PENDING)
1135 {
1136 WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, INFINITE);
1137
1138 RTCritSectEnter(&pThis->CritSect);
1139 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, TRUE /*fWait*/))
1140 rc = VINF_SUCCESS;
1141 else
1142 {
1143 if (pThis->fCancelled)
1144 rc = VERR_CANCELLED;
1145 else
1146 rc = RTErrConvertFromWin32(GetLastError());
1147 break;
1148 }
1149 }
1150 else
1151 {
1152 rc = RTErrConvertFromWin32(GetLastError());
1153 AssertMsgFailedBreak(("%Rrc\n", rc));
1154 }
1155 }
1156 else
1157 {
1158 rc = VERR_CANCELLED;
1159 break;
1160 }
1161
1162 /* Advance. */
1163 cbToRead -= cbRead;
1164 cbTotalRead += cbRead;
1165 pvBuf = (uint8_t *)pvBuf + cbRead;
1166 }
1167
1168 if (pcbRead)
1169 {
1170 *pcbRead = cbTotalRead;
1171 if ( RT_FAILURE(rc)
1172 && cbTotalRead
1173 && rc != VERR_INVALID_POINTER)
1174 rc = VINF_SUCCESS;
1175 }
1176
1177 pThis->Read.hActiveThread = NIL_RTTHREAD;
1178 }
1179 else
1180 rc = VERR_WRONG_ORDER;
1181 rtLocalIpcSessionReleaseAndUnlock(pThis);
1182 }
1183
1184 return rc;
1185}
1186
1187
1188RTDECL(int) RTLocalIpcSessionReadNB(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
1189{
1190 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1191 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1192 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1193 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1194 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
1195 *pcbRead = 0;
1196
1197 int rc = RTCritSectEnter(&pThis->CritSect);
1198 if (RT_SUCCESS(rc))
1199 {
1200 rtLocalIpcSessionRetain(pThis);
1201 if (pThis->Read.hActiveThread == NIL_RTTHREAD)
1202 {
1203 pThis->Read.hActiveThread = RTThreadSelf();
1204
1205 for (;;)
1206 {
1207 DWORD cbRead = 0;
1208 if (!pThis->fCancelled)
1209 {
1210 /*
1211 * Wait for pending zero byte read, if necessary.
1212 * Note! It cannot easily be cancelled due to concurrent current writes.
1213 */
1214 if (!pThis->fZeroByteRead)
1215 { /* likely */ }
1216 else
1217 {
1218 RTCritSectLeave(&pThis->CritSect);
1219 DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, 0);
1220 RTCritSectEnter(&pThis->CritSect);
1221
1222 rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
1223 if (RT_SUCCESS(rc))
1224 continue;
1225
1226 if (rc == VERR_TIMEOUT)
1227 rc = VINF_TRY_AGAIN;
1228 break;
1229 }
1230
1231 /*
1232 * Figure out how much we can read (cannot try and cancel here
1233 * like in the anonymous pipe code).
1234 */
1235 DWORD cbAvailable;
1236 if (PeekNamedPipe(pThis->hNmPipe, NULL, 0, NULL, &cbAvailable, NULL))
1237 {
1238 if (cbAvailable == 0 || cbToRead == 0)
1239 {
1240 *pcbRead = 0;
1241 rc = VINF_TRY_AGAIN;
1242 break;
1243 }
1244 }
1245 else
1246 {
1247 rc = RTErrConvertFromWin32(GetLastError());
1248 break;
1249 }
1250 if (cbAvailable > cbToRead)
1251 cbAvailable = (DWORD)cbToRead;
1252
1253 /*
1254 * Kick of a an overlapped read. It should return immediately, so we
1255 * don't really need to leave the critsect here.
1256 */
1257 rc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(rc == TRUE);
1258 if (ReadFile(pThis->hNmPipe, pvBuf, cbAvailable, &cbRead, &pThis->Read.OverlappedIO))
1259 {
1260 *pcbRead = cbRead;
1261 rc = VINF_SUCCESS;
1262 }
1263 else if (GetLastError() == ERROR_IO_PENDING)
1264 {
1265 DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, 0);
1266 if (rcWait == WAIT_TIMEOUT)
1267 {
1268 RTCritSectLeave(&pThis->CritSect);
1269 rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, INFINITE);
1270 RTCritSectEnter(&pThis->CritSect);
1271 }
1272 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, TRUE /*fWait*/))
1273 {
1274 *pcbRead = cbRead;
1275 rc = VINF_SUCCESS;
1276 }
1277 else
1278 {
1279 if (pThis->fCancelled)
1280 rc = VERR_CANCELLED;
1281 else
1282 rc = RTErrConvertFromWin32(GetLastError());
1283 }
1284 }
1285 else
1286 {
1287 rc = RTErrConvertFromWin32(GetLastError());
1288 AssertMsgFailedBreak(("%Rrc\n", rc));
1289 }
1290 }
1291 else
1292 rc = VERR_CANCELLED;
1293 break;
1294 }
1295
1296 pThis->Read.hActiveThread = NIL_RTTHREAD;
1297 }
1298 else
1299 rc = VERR_WRONG_ORDER;
1300 rtLocalIpcSessionReleaseAndUnlock(pThis);
1301 }
1302
1303 return rc;
1304}
1305
1306
1307#if 0 /* Non-blocking writes are not yet supported. */
1308/**
1309 * Common worker for handling I/O completion.
1310 *
1311 * This is used by RTLocalIpcSessionClose and RTLocalIpcSessionWrite.
1312 *
1313 * @returns IPRT status code.
1314 * @param pThis The pipe instance handle.
1315 */
1316static int rtLocalIpcSessionWriteCheckCompletion(PRTLOCALIPCSESSIONINT pThis)
1317{
1318 int rc;
1319 DWORD rcWait = WaitForSingleObject(pThis->OverlappedIO.hEvent, 0);
1320 if (rcWait == WAIT_OBJECT_0)
1321 {
1322 DWORD cbWritten = 0;
1323 if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &cbWritten, TRUE))
1324 {
1325 for (;;)
1326 {
1327 if (cbWritten >= pThis->cbBounceBufUsed)
1328 {
1329 pThis->fIOPending = false;
1330 rc = VINF_SUCCESS;
1331 break;
1332 }
1333
1334 /* resubmit the remainder of the buffer - can this actually happen? */
1335 memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
1336 rc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(rc == TRUE);
1337 if (!WriteFile(pThis->hNmPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
1338 &cbWritten, &pThis->OverlappedIO))
1339 {
1340 DWORD dwErr = GetLastError();
1341 if (dwErr == ERROR_IO_PENDING)
1342 rc = VINF_TRY_AGAIN;
1343 else
1344 {
1345 pThis->fIOPending = false;
1346 if (dwErr == ERROR_NO_DATA)
1347 rc = VERR_BROKEN_PIPE;
1348 else
1349 rc = RTErrConvertFromWin32(dwErr);
1350 }
1351 break;
1352 }
1353 Assert(cbWritten > 0);
1354 }
1355 }
1356 else
1357 {
1358 pThis->fIOPending = false;
1359 rc = RTErrConvertFromWin32(GetLastError());
1360 }
1361 }
1362 else if (rcWait == WAIT_TIMEOUT)
1363 rc = VINF_TRY_AGAIN;
1364 else
1365 {
1366 pThis->fIOPending = false;
1367 if (rcWait == WAIT_ABANDONED)
1368 rc = VERR_INVALID_HANDLE;
1369 else
1370 rc = RTErrConvertFromWin32(GetLastError());
1371 }
1372 return rc;
1373}
1374#endif
1375
1376
1377RTDECL(int) RTLocalIpcSessionWrite(RTLOCALIPCSESSION hSession, const void *pvBuf, size_t cbToWrite)
1378{
1379 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1380 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1381 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1382 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1383 AssertReturn(cbToWrite, VERR_INVALID_PARAMETER);
1384
1385 int rc = RTCritSectEnter(&pThis->CritSect);
1386 if (RT_SUCCESS(rc))
1387 {
1388 rtLocalIpcSessionRetain(pThis);
1389 if (pThis->Write.hActiveThread == NIL_RTTHREAD)
1390 {
1391 pThis->Write.hActiveThread = RTThreadSelf();
1392
1393 /*
1394 * Try write everything. No bounce buffering necessary.
1395 */
1396 size_t cbTotalWritten = 0;
1397 while (cbToWrite > 0)
1398 {
1399 DWORD cbWritten = 0;
1400 if (!pThis->fCancelled)
1401 {
1402 BOOL fRc = ResetEvent(pThis->Write.OverlappedIO.hEvent); Assert(fRc == TRUE);
1403 RTCritSectLeave(&pThis->CritSect);
1404
1405 DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0;
1406 fRc = WriteFile(pThis->hNmPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Write.OverlappedIO);
1407 if (fRc)
1408 rc = VINF_SUCCESS;
1409 else
1410 {
1411 DWORD dwErr = GetLastError();
1412 if (dwErr == ERROR_IO_PENDING)
1413 {
1414 DWORD rcWait = WaitForSingleObject(pThis->Write.OverlappedIO.hEvent, INFINITE);
1415 if (rcWait == WAIT_OBJECT_0)
1416 {
1417 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Write.OverlappedIO, &cbWritten, TRUE /*fWait*/))
1418 rc = VINF_SUCCESS;
1419 else
1420 rc = RTErrConvertFromWin32(GetLastError());
1421 }
1422 else if (rcWait == WAIT_TIMEOUT)
1423 rc = VERR_TIMEOUT;
1424 else if (rcWait == WAIT_ABANDONED)
1425 rc = VERR_INVALID_HANDLE;
1426 else
1427 rc = RTErrConvertFromWin32(GetLastError());
1428 }
1429 else if (dwErr == ERROR_NO_DATA)
1430 rc = VERR_BROKEN_PIPE;
1431 else
1432 rc = RTErrConvertFromWin32(dwErr);
1433 }
1434
1435 if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */
1436 cbWritten = cbToWriteInThisIteration;
1437
1438 RTCritSectEnter(&pThis->CritSect);
1439 if (RT_FAILURE(rc))
1440 break;
1441 }
1442 else
1443 {
1444 rc = VERR_CANCELLED;
1445 break;
1446 }
1447
1448 /* Advance. */
1449 pvBuf = (char const *)pvBuf + cbWritten;
1450 cbTotalWritten += cbWritten;
1451 cbToWrite -= cbWritten;
1452 }
1453
1454 pThis->Write.hActiveThread = NIL_RTTHREAD;
1455 }
1456 else
1457 rc = VERR_WRONG_ORDER;
1458 rtLocalIpcSessionReleaseAndUnlock(pThis);
1459 }
1460
1461 return rc;
1462}
1463
1464
1465RTDECL(int) RTLocalIpcSessionFlush(RTLOCALIPCSESSION hSession)
1466{
1467 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1468 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1469 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1470
1471 int rc = RTCritSectEnter(&pThis->CritSect);
1472 if (RT_SUCCESS(rc))
1473 {
1474 if (pThis->Write.hActiveThread == NIL_RTTHREAD)
1475 {
1476 /* No flushing on Windows needed since RTLocalIpcSessionWrite will block until
1477 * all data was written (or an error occurred). */
1478 /** @todo r=bird: above comment is misinformed.
1479 * Implement this as soon as we want an explicit asynchronous version of
1480 * RTLocalIpcSessionWrite on Windows. */
1481 rc = VINF_SUCCESS;
1482 }
1483 else
1484 rc = VERR_WRONG_ORDER;
1485 RTCritSectLeave(&pThis->CritSect);
1486 }
1487 return rc;
1488}
1489
1490
1491RTDECL(int) RTLocalIpcSessionWaitForData(RTLOCALIPCSESSION hSession, uint32_t cMillies)
1492{
1493 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1494 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1495 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1496
1497 uint64_t const msStart = RTTimeMilliTS();
1498
1499 int rc = RTCritSectEnter(&pThis->CritSect);
1500 if (RT_SUCCESS(rc))
1501 {
1502 rtLocalIpcSessionRetain(pThis);
1503 if (pThis->Read.hActiveThread == NIL_RTTHREAD)
1504 {
1505 pThis->Read.hActiveThread = RTThreadSelf();
1506
1507 /*
1508 * Wait loop.
1509 */
1510 for (unsigned iLoop = 0;; iLoop++)
1511 {
1512 /*
1513 * Check for cancellation before we continue.
1514 */
1515 if (!pThis->fCancelled)
1516 { /* likely */ }
1517 else
1518 {
1519 rc = VERR_CANCELLED;
1520 break;
1521 }
1522
1523 /*
1524 * Prep something we can wait on.
1525 */
1526 HANDLE hWait = INVALID_HANDLE_VALUE;
1527 if (pThis->fZeroByteRead)
1528 hWait = pThis->Read.OverlappedIO.hEvent;
1529 else
1530 {
1531 /* Peek at the pipe buffer and see how many bytes it contains. */
1532 DWORD cbAvailable;
1533 if ( PeekNamedPipe(pThis->hNmPipe, NULL, 0, NULL, &cbAvailable, NULL)
1534 && cbAvailable)
1535 {
1536 rc = VINF_SUCCESS;
1537 break;
1538 }
1539
1540 /* Start a zero byte read operation that we can wait on. */
1541 if (cMillies == 0)
1542 {
1543 rc = VERR_TIMEOUT;
1544 break;
1545 }
1546 BOOL fRc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(fRc == TRUE); NOREF(fRc);
1547 DWORD cbRead = 0;
1548 if (ReadFile(pThis->hNmPipe, pThis->abBuf, 0 /*cbToRead*/, &cbRead, &pThis->Read.OverlappedIO))
1549 {
1550 rc = VINF_SUCCESS;
1551 if (iLoop > 10)
1552 RTThreadYield();
1553 }
1554 else if (GetLastError() == ERROR_IO_PENDING)
1555 {
1556 pThis->fZeroByteRead = true;
1557 hWait = pThis->Read.OverlappedIO.hEvent;
1558 }
1559 else
1560 rc = RTErrConvertFromWin32(GetLastError());
1561 if (RT_FAILURE(rc))
1562 break;
1563 }
1564
1565 /*
1566 * Check for timeout.
1567 */
1568 DWORD cMsMaxWait = INFINITE; /* (MSC maybe used uninitialized) */
1569 if (cMillies == RT_INDEFINITE_WAIT)
1570 cMsMaxWait = INFINITE;
1571 else if ( hWait != INVALID_HANDLE_VALUE
1572 || iLoop > 10)
1573 {
1574 uint64_t cMsElapsed = RTTimeMilliTS() - msStart;
1575 if (cMsElapsed <= cMillies)
1576 cMsMaxWait = cMillies - (uint32_t)cMsElapsed;
1577 else if (iLoop == 0)
1578 cMsMaxWait = cMillies ? 1 : 0;
1579 else
1580 {
1581 rc = VERR_TIMEOUT;
1582 break;
1583 }
1584 }
1585
1586 /*
1587 * Wait and collect the result.
1588 */
1589 if (hWait != INVALID_HANDLE_VALUE)
1590 {
1591 RTCritSectLeave(&pThis->CritSect);
1592
1593 DWORD rcWait = WaitForSingleObject(hWait, cMsMaxWait);
1594
1595 int rc2 = RTCritSectEnter(&pThis->CritSect);
1596 AssertRC(rc2);
1597
1598 rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
1599 break;
1600 }
1601 }
1602
1603 pThis->Read.hActiveThread = NIL_RTTHREAD;
1604 }
1605
1606 rtLocalIpcSessionReleaseAndUnlock(pThis);
1607 }
1608
1609 return rc;
1610}
1611
1612
1613RTDECL(int) RTLocalIpcSessionCancel(RTLOCALIPCSESSION hSession)
1614{
1615 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1616 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1617 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1618
1619 /*
1620 * Enter the critical section, then set the cancellation flag
1621 * and signal the event (to wake up anyone in/at WaitForSingleObject).
1622 */
1623 int rc = RTCritSectEnter(&pThis->CritSect);
1624 if (RT_SUCCESS(rc))
1625 {
1626 rtLocalIpcSessionRetain(pThis);
1627 rc = rtLocalIpcWinCancel(pThis);
1628 rtLocalIpcSessionReleaseAndUnlock(pThis);
1629 }
1630
1631 return rc;
1632}
1633
1634
1635RTDECL(int) RTLocalIpcSessionQueryProcess(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess)
1636{
1637 RT_NOREF_PV(hSession); RT_NOREF_PV(pProcess);
1638 return VERR_NOT_SUPPORTED;
1639}
1640
1641
1642RTDECL(int) RTLocalIpcSessionQueryUserId(RTLOCALIPCSESSION hSession, PRTUID pUid)
1643{
1644 RT_NOREF_PV(hSession); RT_NOREF_PV(pUid);
1645 return VERR_NOT_SUPPORTED;
1646}
1647
1648
1649RTDECL(int) RTLocalIpcSessionQueryGroupId(RTLOCALIPCSESSION hSession, PRTGID pGid)
1650{
1651 RT_NOREF_PV(hSession); RT_NOREF_PV(pGid);
1652 return VERR_NOT_SUPPORTED;
1653}
1654
Note: See TracBrowser for help on using the repository browser.

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