VirtualBox

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

Last change on this file since 96476 was 96407, checked in by vboxsync, 3 years ago

scm copyright and license note update

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