VirtualBox

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

最後變更 在這個檔案從96507是 96505,由 vboxsync 提交於 3 年 前

IPRT: Dynamically resolve GetProcessAffinityMask, SetThreadAffinityMask, CreateIoCompletionPort, GetQueuedCompletionStatus and PostQueuedCompletionStatus too so related (ValKit) code loads on ancient NT versions. bugref:10261

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

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