VirtualBox

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

最後變更 在這個檔案從58300是 58300,由 vboxsync 提交於 9 年 前

RTLocalIpc: Added RTLocalIpcSessionRetain and RTLocalIpcSessionRelease.

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

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