VirtualBox

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

Last change on this file since 74460 was 74460, checked in by vboxsync, 6 years ago

IPRT: Implemented long filename support for windows (except for LoadLibrary). bugref:9248

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.0 KB
Line 
1/* $Id: localipc-win.cpp 74460 2018-09-25 15:42:33Z vboxsync $ */
2/** @file
3 * IPRT - Local IPC, Windows Implementation Using Named Pipes.
4 */
5
6/*
7 * Copyright (C) 2008-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_LOCALIPC
32/*
33 * We have to force NT 5.0 here because of
34 * ConvertStringSecurityDescriptorToSecurityDescriptor. Note that because of
35 * FILE_FLAG_FIRST_PIPE_INSTANCE this code actually requires W2K SP2+.
36 */
37#ifndef _WIN32_WINNT
38# define _WIN32_WINNT 0x0500 /* for ConvertStringSecurityDescriptorToSecurityDescriptor */
39#elif _WIN32_WINNT < 0x0500
40# undef _WIN32_WINNT
41# define _WIN32_WINNT 0x0500
42#endif
43#define UNICODE /* For the SDDL_ strings. */
44#include <iprt/win/windows.h>
45#include <sddl.h>
46
47#include "internal/iprt.h"
48#include <iprt/localipc.h>
49
50#include <iprt/asm.h>
51#include <iprt/assert.h>
52#include <iprt/critsect.h>
53#include <iprt/ctype.h>
54#include <iprt/err.h>
55#include <iprt/ldr.h>
56#include <iprt/log.h>
57#include <iprt/mem.h>
58#include <iprt/param.h>
59#include <iprt/string.h>
60#include <iprt/thread.h>
61#include <iprt/time.h>
62
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. ASSUMES reasonable length
369 * for now, so no long path prefixing needed.
370 * @param pwszFullName The output buffer.
371 * @param cwcFullName The output buffer size excluding the terminator.
372 * @param fNative Whether the user supplied name is a native or
373 * portable one.
374 */
375static int rtLocalIpcWinConstructName(const char *pszName, PRTUTF16 pwszFullName, size_t cwcFullName, bool fNative)
376{
377 if (!fNative)
378 {
379 static RTUTF16 const s_wszPrefix[] = RTLOCALIPC_WIN_PREFIX;
380 Assert(cwcFullName * sizeof(RTUTF16) > sizeof(s_wszPrefix));
381 memcpy(pwszFullName, s_wszPrefix, sizeof(s_wszPrefix));
382 cwcFullName -= RT_ELEMENTS(s_wszPrefix) - 1;
383 pwszFullName += RT_ELEMENTS(s_wszPrefix) - 1;
384 }
385 return RTStrToUtf16Ex(pszName, RTSTR_MAX, &pwszFullName, cwcFullName + 1, NULL);
386}
387
388
389RTDECL(int) RTLocalIpcServerCreate(PRTLOCALIPCSERVER phServer, const char *pszName, uint32_t fFlags)
390{
391 /*
392 * Validate parameters.
393 */
394 AssertPtrReturn(phServer, VERR_INVALID_POINTER);
395 *phServer = NIL_RTLOCALIPCSERVER;
396 AssertReturn(!(fFlags & ~RTLOCALIPC_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
397 size_t cwcFullName;
398 int rc = rtLocalIpcWinValidateName(pszName, &cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
399 if (RT_SUCCESS(rc))
400 {
401 /*
402 * Allocate and initialize the instance data.
403 */
404 size_t cbThis = RT_UOFFSETOF_DYN(RTLOCALIPCSERVERINT, wszName[cwcFullName + 1]);
405 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)RTMemAllocVar(cbThis);
406 AssertReturn(pThis, VERR_NO_MEMORY);
407
408 pThis->u32Magic = RTLOCALIPCSERVER_MAGIC;
409 pThis->cRefs = 1; /* the one we return */
410 pThis->fCancelled = false;
411
412 rc = rtLocalIpcWinConstructName(pszName, pThis->wszName, cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
413 if (RT_SUCCESS(rc))
414 {
415 rc = RTCritSectInit(&pThis->CritSect);
416 if (RT_SUCCESS(rc))
417 {
418 pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
419 FALSE /*bInitialState*/, NULL /*lpName*/);
420 if (pThis->hEvent != NULL)
421 {
422 RT_ZERO(pThis->OverlappedIO);
423 pThis->OverlappedIO.Internal = STATUS_PENDING;
424 pThis->OverlappedIO.hEvent = pThis->hEvent;
425
426 rc = rtLocalIpcServerWinCreatePipeInstance(&pThis->hNmPipe, pThis->wszName, true /* fFirst */);
427 if (RT_SUCCESS(rc))
428 {
429 *phServer = pThis;
430 return VINF_SUCCESS;
431 }
432
433 BOOL fRc = CloseHandle(pThis->hEvent);
434 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
435 }
436 else
437 rc = RTErrConvertFromWin32(GetLastError());
438
439 int rc2 = RTCritSectDelete(&pThis->CritSect);
440 AssertRC(rc2);
441 }
442 }
443 RTMemFree(pThis);
444 }
445 return rc;
446}
447
448
449/**
450 * Retains a reference to the server instance.
451 *
452 * @returns
453 * @param pThis The server instance.
454 */
455DECLINLINE(void) rtLocalIpcServerRetain(PRTLOCALIPCSERVERINT pThis)
456{
457 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
458 Assert(cRefs < UINT32_MAX / 2 && cRefs); NOREF(cRefs);
459}
460
461
462/**
463 * Call when the reference count reaches 0.
464 *
465 * Caller owns the critsect.
466 *
467 * @returns VINF_OBJECT_DESTROYED
468 * @param pThis The instance to destroy.
469 */
470DECL_NO_INLINE(static, int) rtLocalIpcServerWinDestroy(PRTLOCALIPCSERVERINT pThis)
471{
472 Assert(pThis->u32Magic == ~RTLOCALIPCSERVER_MAGIC);
473 pThis->u32Magic = ~RTLOCALIPCSERVER_MAGIC;
474
475 BOOL fRc = CloseHandle(pThis->hNmPipe);
476 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
477 pThis->hNmPipe = INVALID_HANDLE_VALUE;
478
479 fRc = CloseHandle(pThis->hEvent);
480 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
481 pThis->hEvent = NULL;
482
483 RTCritSectLeave(&pThis->CritSect);
484 RTCritSectDelete(&pThis->CritSect);
485
486 RTMemFree(pThis);
487 return VINF_OBJECT_DESTROYED;
488}
489
490
491/**
492 * Server instance destructor.
493 *
494 * @returns VINF_OBJECT_DESTROYED
495 * @param pThis The server instance.
496 */
497DECL_NO_INLINE(static, int) rtLocalIpcServerDtor(PRTLOCALIPCSERVERINT pThis)
498{
499 RTCritSectEnter(&pThis->CritSect);
500 return rtLocalIpcServerWinDestroy(pThis);
501}
502
503
504/**
505 * Releases a reference to the server instance.
506 *
507 * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed.
508 * @param pThis The server instance.
509 */
510DECLINLINE(int) rtLocalIpcServerRelease(PRTLOCALIPCSERVERINT pThis)
511{
512 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
513 Assert(cRefs < UINT32_MAX / 2);
514 if (!cRefs)
515 return rtLocalIpcServerDtor(pThis);
516 return VINF_SUCCESS;
517}
518
519
520/**
521 * Releases a reference to the server instance and leaves the critsect.
522 *
523 * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed.
524 * @param pThis The server instance.
525 */
526DECLINLINE(int) rtLocalIpcServerReleaseAndUnlock(PRTLOCALIPCSERVERINT pThis)
527{
528 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
529 Assert(cRefs < UINT32_MAX / 2);
530 if (!cRefs)
531 return rtLocalIpcServerWinDestroy(pThis);
532 return RTCritSectLeave(&pThis->CritSect);
533}
534
535
536
537RTDECL(int) RTLocalIpcServerDestroy(RTLOCALIPCSERVER hServer)
538{
539 /*
540 * Validate input.
541 */
542 if (hServer == NIL_RTLOCALIPCSERVER)
543 return VINF_SUCCESS;
544 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
545 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
546 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
547
548 /*
549 * Cancel any thread currently busy using the server,
550 * leaving the cleanup to it.
551 */
552 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTLOCALIPCSERVER_MAGIC, RTLOCALIPCSERVER_MAGIC), VERR_WRONG_ORDER);
553
554 RTCritSectEnter(&pThis->CritSect);
555
556 /* Cancel everything. */
557 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
558 if (pThis->cRefs > 1)
559 {
560 BOOL fRc = SetEvent(pThis->hEvent);
561 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
562 }
563
564 return rtLocalIpcServerReleaseAndUnlock(pThis);
565}
566
567
568RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION phClientSession)
569{
570 /*
571 * Validate input.
572 */
573 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
574 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
575 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
576 AssertPtrReturn(phClientSession, VERR_INVALID_POINTER);
577
578 /*
579 * Enter the critsect before inspecting the object further.
580 */
581 int rc = RTCritSectEnter(&pThis->CritSect);
582 AssertRCReturn(rc, rc);
583
584 rtLocalIpcServerRetain(pThis);
585 if (!pThis->fCancelled)
586 {
587 ResetEvent(pThis->hEvent);
588
589 RTCritSectLeave(&pThis->CritSect);
590
591 /*
592 * Try connect a client. We need to use overlapped I/O here because
593 * of the cancellation done by RTLocalIpcServerCancel and RTLocalIpcServerDestroy.
594 */
595 SetLastError(NO_ERROR);
596 BOOL fRc = ConnectNamedPipe(pThis->hNmPipe, &pThis->OverlappedIO);
597 DWORD dwErr = fRc ? NO_ERROR : GetLastError();
598 if ( !fRc
599 && dwErr == ERROR_IO_PENDING)
600 {
601 WaitForSingleObject(pThis->hEvent, INFINITE);
602 DWORD dwIgnored;
603 fRc = GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &dwIgnored, FALSE /* bWait*/);
604 dwErr = fRc ? NO_ERROR : GetLastError();
605 }
606
607 RTCritSectEnter(&pThis->CritSect);
608 if ( !pThis->fCancelled /* Event signalled but not cancelled? */
609 && pThis->u32Magic == RTLOCALIPCSERVER_MAGIC)
610 {
611 /*
612 * Still alive, some error or an actual client.
613 *
614 * If it's the latter we'll have to create a new pipe instance that
615 * replaces the current one for the server. The current pipe instance
616 * will be assigned to the client session.
617 */
618 if ( fRc
619 || dwErr == ERROR_PIPE_CONNECTED)
620 {
621 HANDLE hNmPipe;
622 rc = rtLocalIpcServerWinCreatePipeInstance(&hNmPipe, pThis->wszName, false /* fFirst */);
623 if (RT_SUCCESS(rc))
624 {
625 HANDLE hNmPipeSession = pThis->hNmPipe; /* consumed */
626 pThis->hNmPipe = hNmPipe;
627 rc = rtLocalIpcWinCreateSession(phClientSession, hNmPipeSession);
628 }
629 else
630 {
631 /*
632 * We failed to create a new instance for the server, disconnect
633 * the client and fail. Don't try service the client here.
634 */
635 fRc = DisconnectNamedPipe(pThis->hNmPipe);
636 AssertMsg(fRc, ("%d\n", GetLastError()));
637 }
638 }
639 else
640 rc = RTErrConvertFromWin32(dwErr);
641 }
642 else
643 {
644 /*
645 * Cancelled.
646 *
647 * Cancel the overlapped io if it didn't complete (must be done
648 * in the this thread) or disconnect the client.
649 */
650 Assert(pThis->fCancelled);
651 if ( fRc
652 || dwErr == ERROR_PIPE_CONNECTED)
653 fRc = DisconnectNamedPipe(pThis->hNmPipe);
654 else if (dwErr == ERROR_IO_PENDING)
655 fRc = CancelIo(pThis->hNmPipe);
656 else
657 fRc = TRUE;
658 AssertMsg(fRc, ("%d\n", GetLastError()));
659 rc = VERR_CANCELLED;
660 }
661 }
662 else
663 {
664 /*pThis->fCancelled = false; - Terrible interface idea. Add API to clear fCancelled if ever required. */
665 rc = VERR_CANCELLED;
666 }
667 rtLocalIpcServerReleaseAndUnlock(pThis);
668 return rc;
669}
670
671
672RTDECL(int) RTLocalIpcServerCancel(RTLOCALIPCSERVER hServer)
673{
674 /*
675 * Validate input.
676 */
677 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
678 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
679 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
680
681 /*
682 * Enter the critical section, then set the cancellation flag
683 * and signal the event (to wake up anyone in/at WaitForSingleObject).
684 */
685 rtLocalIpcServerRetain(pThis);
686 int rc = RTCritSectEnter(&pThis->CritSect);
687 if (RT_SUCCESS(rc))
688 {
689 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
690
691 BOOL fRc = SetEvent(pThis->hEvent);
692 if (fRc)
693 rc = VINF_SUCCESS;
694 else
695 {
696 DWORD dwErr = GetLastError();
697 AssertMsgFailed(("dwErr=%u\n", dwErr));
698 rc = RTErrConvertFromWin32(dwErr);
699 }
700
701 rtLocalIpcServerReleaseAndUnlock(pThis);
702 }
703 else
704 rtLocalIpcServerRelease(pThis);
705 return rc;
706}
707
708
709/**
710 * Create a session instance for a new server client or a client connect.
711 *
712 * @returns IPRT status code.
713 *
714 * @param ppSession Where to store the session handle on success.
715 * @param hNmPipeSession The named pipe handle if server calling,
716 * INVALID_HANDLE_VALUE if client connect. This will
717 * be consumed by this session, meaning on failure to
718 * create the session it will be closed.
719 */
720static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSIONINT *ppSession, HANDLE hNmPipeSession)
721{
722 AssertPtr(ppSession);
723
724 /*
725 * Allocate and initialize the session instance data.
726 */
727 int rc;
728 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAllocZ(sizeof(*pThis));
729 if (pThis)
730 {
731 pThis->u32Magic = RTLOCALIPCSESSION_MAGIC;
732 pThis->cRefs = 1; /* our ref */
733 pThis->fCancelled = false;
734 pThis->fZeroByteRead = false;
735 pThis->fServerSide = hNmPipeSession != INVALID_HANDLE_VALUE;
736 pThis->hNmPipe = hNmPipeSession;
737#if 0 /* Non-blocking writes are not yet supported. */
738 pThis->pbBounceBuf = NULL;
739 pThis->cbBounceBufAlloc = 0;
740 pThis->cbBounceBufUsed = 0;
741#endif
742 rc = RTCritSectInit(&pThis->CritSect);
743 if (RT_SUCCESS(rc))
744 {
745 pThis->Read.hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
746 FALSE /*bInitialState*/, NULL /*lpName*/);
747 if (pThis->Read.hEvent != NULL)
748 {
749 pThis->Read.OverlappedIO.Internal = STATUS_PENDING;
750 pThis->Read.OverlappedIO.hEvent = pThis->Read.hEvent;
751 pThis->Read.hActiveThread = NIL_RTTHREAD;
752
753 pThis->Write.hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
754 FALSE /*bInitialState*/, NULL /*lpName*/);
755 if (pThis->Write.hEvent != NULL)
756 {
757 pThis->Write.OverlappedIO.Internal = STATUS_PENDING;
758 pThis->Write.OverlappedIO.hEvent = pThis->Write.hEvent;
759 pThis->Write.hActiveThread = NIL_RTTHREAD;
760
761 *ppSession = pThis;
762 return VINF_SUCCESS;
763 }
764
765 CloseHandle(pThis->Read.hEvent);
766 }
767
768 /* bail out */
769 rc = RTErrConvertFromWin32(GetLastError());
770 RTCritSectDelete(&pThis->CritSect);
771 }
772 RTMemFree(pThis);
773 }
774 else
775 rc = VERR_NO_MEMORY;
776
777 if (hNmPipeSession != INVALID_HANDLE_VALUE)
778 {
779 BOOL fRc = CloseHandle(hNmPipeSession);
780 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
781 }
782 return rc;
783}
784
785
786RTDECL(int) RTLocalIpcSessionConnect(PRTLOCALIPCSESSION phSession, const char *pszName, uint32_t fFlags)
787{
788 /*
789 * Validate input.
790 */
791 AssertPtrReturn(phSession, VERR_INVALID_POINTER);
792 AssertReturn(!(fFlags & ~RTLOCALIPC_C_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
793
794 size_t cwcFullName;
795 int rc = rtLocalIpcWinValidateName(pszName, &cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
796 if (RT_SUCCESS(rc))
797 {
798 /*
799 * Create a session (shared with server client session creation).
800 */
801 PRTLOCALIPCSESSIONINT pThis;
802 rc = rtLocalIpcWinCreateSession(&pThis, INVALID_HANDLE_VALUE);
803 if (RT_SUCCESS(rc))
804 {
805 /*
806 * Try open the pipe.
807 */
808 PSECURITY_DESCRIPTOR pSecDesc;
809 rc = rtLocalIpcServerWinAllocSecurityDescriptior(&pSecDesc, false /*fServer*/);
810 if (RT_SUCCESS(rc))
811 {
812 PRTUTF16 pwszFullName = RTUtf16Alloc((cwcFullName + 1) * sizeof(RTUTF16));
813 if (pwszFullName)
814 rc = rtLocalIpcWinConstructName(pszName, pwszFullName, cwcFullName,
815 RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
816 else
817 rc = VERR_NO_UTF16_MEMORY;
818 if (RT_SUCCESS(rc))
819 {
820 SECURITY_ATTRIBUTES SecAttrs;
821 SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
822 SecAttrs.lpSecurityDescriptor = pSecDesc;
823 SecAttrs.bInheritHandle = FALSE;
824
825 HANDLE hPipe = CreateFileW(pwszFullName,
826 GENERIC_READ | GENERIC_WRITE,
827 0 /*no sharing*/,
828 &SecAttrs,
829 OPEN_EXISTING,
830 FILE_FLAG_OVERLAPPED,
831 NULL /*no template handle*/);
832 if (hPipe != INVALID_HANDLE_VALUE)
833 {
834 pThis->hNmPipe = hPipe;
835
836 LocalFree(pSecDesc);
837 RTUtf16Free(pwszFullName);
838
839 /*
840 * We're done!
841 */
842 *phSession = pThis;
843 return VINF_SUCCESS;
844 }
845
846 rc = RTErrConvertFromWin32(GetLastError());
847 }
848
849 RTUtf16Free(pwszFullName);
850 LocalFree(pSecDesc);
851 }
852
853 /* destroy the session handle. */
854 CloseHandle(pThis->Read.hEvent);
855 CloseHandle(pThis->Write.hEvent);
856 RTCritSectDelete(&pThis->CritSect);
857
858 RTMemFree(pThis);
859 }
860 }
861 return rc;
862}
863
864
865/**
866 * Cancells all pending I/O operations, forcing the methods to return with
867 * VERR_CANCELLED (unless they've got actual data to return).
868 *
869 * Used by RTLocalIpcSessionCancel and RTLocalIpcSessionClose.
870 *
871 * @returns IPRT status code.
872 * @param pThis The client session instance.
873 */
874static int rtLocalIpcWinCancel(PRTLOCALIPCSESSIONINT pThis)
875{
876 ASMAtomicUoWriteBool(&pThis->fCancelled, true);
877
878 /*
879 * Call CancelIo since this call cancels both read and write oriented operations.
880 */
881 if ( pThis->fZeroByteRead
882 || pThis->Read.hActiveThread != NIL_RTTHREAD
883 || pThis->Write.hActiveThread != NIL_RTTHREAD)
884 CancelIo(pThis->hNmPipe);
885
886 /*
887 * Set both event semaphores.
888 */
889 BOOL fRc = SetEvent(pThis->Read.hEvent);
890 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
891 fRc = SetEvent(pThis->Write.hEvent);
892 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
893
894 return VINF_SUCCESS;
895}
896
897
898/**
899 * Retains a reference to the session instance.
900 *
901 * @param pThis The client session instance.
902 */
903DECLINLINE(void) rtLocalIpcSessionRetain(PRTLOCALIPCSESSIONINT pThis)
904{
905 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
906 Assert(cRefs < UINT32_MAX / 2 && cRefs); NOREF(cRefs);
907}
908
909
910RTDECL(uint32_t) RTLocalIpcSessionRetain(RTLOCALIPCSESSION hSession)
911{
912 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
913 AssertPtrReturn(pThis, UINT32_MAX);
914 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
915
916 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
917 Assert(cRefs < UINT32_MAX / 2 && cRefs);
918 return cRefs;
919}
920
921
922/**
923 * Call when the reference count reaches 0.
924 *
925 * Caller owns the critsect.
926 *
927 * @returns VINF_OBJECT_DESTROYED
928 * @param pThis The instance to destroy.
929 */
930DECL_NO_INLINE(static, int) rtLocalIpcSessionWinDestroy(PRTLOCALIPCSESSIONINT pThis)
931{
932 BOOL fRc = CloseHandle(pThis->hNmPipe);
933 AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
934 pThis->hNmPipe = INVALID_HANDLE_VALUE;
935
936 fRc = CloseHandle(pThis->Write.hEvent);
937 AssertMsg(fRc, ("%d\n", GetLastError()));
938 pThis->Write.hEvent = NULL;
939
940 fRc = CloseHandle(pThis->Read.hEvent);
941 AssertMsg(fRc, ("%d\n", GetLastError()));
942 pThis->Read.hEvent = NULL;
943
944 int rc2 = RTCritSectLeave(&pThis->CritSect); AssertRC(rc2);
945 RTCritSectDelete(&pThis->CritSect);
946
947 RTMemFree(pThis);
948 return VINF_OBJECT_DESTROYED;
949}
950
951
952/**
953 * Releases a reference to the session instance and unlock it.
954 *
955 * @returns VINF_SUCCESS or VINF_OBJECT_DESTROYED as appropriate.
956 * @param pThis The session instance.
957 */
958DECLINLINE(int) rtLocalIpcSessionReleaseAndUnlock(PRTLOCALIPCSESSIONINT pThis)
959{
960 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
961 Assert(cRefs < UINT32_MAX / 2);
962 if (!cRefs)
963 return rtLocalIpcSessionWinDestroy(pThis);
964
965 int rc2 = RTCritSectLeave(&pThis->CritSect); AssertRC(rc2);
966 Log(("rtLocalIpcSessionReleaseAndUnlock: %u refs left\n", cRefs));
967 return VINF_SUCCESS;
968}
969
970
971RTDECL(uint32_t) RTLocalIpcSessionRelease(RTLOCALIPCSESSION hSession)
972{
973 if (hSession == NIL_RTLOCALIPCSESSION)
974 return 0;
975
976 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
977 AssertPtrReturn(pThis, UINT32_MAX);
978 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
979
980 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
981 Assert(cRefs < UINT32_MAX / 2);
982 if (cRefs)
983 Log(("RTLocalIpcSessionRelease: %u refs left\n", cRefs));
984 else
985 {
986 RTCritSectEnter(&pThis->CritSect);
987 rtLocalIpcSessionWinDestroy(pThis);
988 }
989 return cRefs;
990}
991
992
993RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession)
994{
995 /*
996 * Validate input.
997 */
998 if (hSession == NIL_RTLOCALIPCSESSION)
999 return VINF_SUCCESS;
1000 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1001 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1002 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1003
1004 /*
1005 * Invalidate the instance, cancel all outstanding I/O and drop our reference.
1006 */
1007 RTCritSectEnter(&pThis->CritSect);
1008 rtLocalIpcWinCancel(pThis);
1009 return rtLocalIpcSessionReleaseAndUnlock(pThis);
1010}
1011
1012
1013/**
1014 * Handles WaitForSingleObject return value when waiting for a zero byte read.
1015 *
1016 * The zero byte read is started by the RTLocalIpcSessionWaitForData method and
1017 * left pending when the function times out. This saves us the problem of
1018 * CancelIo messing with all active I/O operations and the trouble of restarting
1019 * the zero byte read the next time the method is called. However should
1020 * RTLocalIpcSessionRead be called after a failed RTLocalIpcSessionWaitForData
1021 * call, the zero byte read will still be pending and it must wait for it to
1022 * complete before the OVERLAPPEDIO structure can be reused.
1023 *
1024 * Thus, both functions will do WaitForSingleObject and share this routine to
1025 * handle the outcome.
1026 *
1027 * @returns IPRT status code.
1028 * @param pThis The session instance.
1029 * @param rcWait The WaitForSingleObject return code.
1030 */
1031static int rtLocalIpcWinGetZeroReadResult(PRTLOCALIPCSESSIONINT pThis, DWORD rcWait)
1032{
1033 int rc;
1034 DWORD cbRead = 42;
1035 if (rcWait == WAIT_OBJECT_0)
1036 {
1037 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, !pThis->fCancelled /*fWait*/))
1038 {
1039 Assert(cbRead == 0);
1040 rc = VINF_SUCCESS;
1041 pThis->fZeroByteRead = false;
1042 }
1043 else if (pThis->fCancelled)
1044 rc = VERR_CANCELLED;
1045 else
1046 rc = RTErrConvertFromWin32(GetLastError());
1047 }
1048 else
1049 {
1050 /* We try get the result here too, just in case we're lucky, but no waiting. */
1051 DWORD dwErr = GetLastError();
1052 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, FALSE /*fWait*/))
1053 {
1054 Assert(cbRead == 0);
1055 rc = VINF_SUCCESS;
1056 pThis->fZeroByteRead = false;
1057 }
1058 else if (rcWait == WAIT_TIMEOUT)
1059 rc = VERR_TIMEOUT;
1060 else if (rcWait == WAIT_ABANDONED)
1061 rc = VERR_INVALID_HANDLE;
1062 else
1063 rc = RTErrConvertFromWin32(dwErr);
1064 }
1065 return rc;
1066}
1067
1068
1069RTDECL(int) RTLocalIpcSessionRead(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
1070{
1071 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1072 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1073 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1074 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1075 /* pcbRead is optional. */
1076
1077 int rc = RTCritSectEnter(&pThis->CritSect);
1078 if (RT_SUCCESS(rc))
1079 {
1080 rtLocalIpcSessionRetain(pThis);
1081 if (pThis->Read.hActiveThread == NIL_RTTHREAD)
1082 {
1083 pThis->Read.hActiveThread = RTThreadSelf();
1084
1085 size_t cbTotalRead = 0;
1086 while (cbToRead > 0)
1087 {
1088 DWORD cbRead = 0;
1089 if (!pThis->fCancelled)
1090 {
1091 /*
1092 * Wait for pending zero byte read, if necessary.
1093 * Note! It cannot easily be cancelled due to concurrent current writes.
1094 */
1095 if (!pThis->fZeroByteRead)
1096 { /* likely */ }
1097 else
1098 {
1099 RTCritSectLeave(&pThis->CritSect);
1100 DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, RT_MS_1MIN);
1101 RTCritSectEnter(&pThis->CritSect);
1102
1103 rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
1104 if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT)
1105 continue;
1106 break;
1107 }
1108
1109 /*
1110 * Kick of a an overlapped read. It should return immediately if
1111 * there is bytes in the buffer. If not, we'll cancel it and see
1112 * what we get back.
1113 */
1114 rc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(rc == TRUE);
1115 RTCritSectLeave(&pThis->CritSect);
1116
1117 if (ReadFile(pThis->hNmPipe, pvBuf,
1118 cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
1119 &cbRead, &pThis->Read.OverlappedIO))
1120 {
1121 RTCritSectEnter(&pThis->CritSect);
1122 rc = VINF_SUCCESS;
1123 }
1124 else if (GetLastError() == ERROR_IO_PENDING)
1125 {
1126 WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, INFINITE);
1127
1128 RTCritSectEnter(&pThis->CritSect);
1129 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, TRUE /*fWait*/))
1130 rc = VINF_SUCCESS;
1131 else
1132 {
1133 if (pThis->fCancelled)
1134 rc = VERR_CANCELLED;
1135 else
1136 rc = RTErrConvertFromWin32(GetLastError());
1137 break;
1138 }
1139 }
1140 else
1141 {
1142 rc = RTErrConvertFromWin32(GetLastError());
1143 AssertMsgFailedBreak(("%Rrc\n", rc));
1144 }
1145 }
1146 else
1147 {
1148 rc = VERR_CANCELLED;
1149 break;
1150 }
1151
1152 /* Advance. */
1153 cbToRead -= cbRead;
1154 cbTotalRead += cbRead;
1155 pvBuf = (uint8_t *)pvBuf + cbRead;
1156 }
1157
1158 if (pcbRead)
1159 {
1160 *pcbRead = cbTotalRead;
1161 if ( RT_FAILURE(rc)
1162 && cbTotalRead
1163 && rc != VERR_INVALID_POINTER)
1164 rc = VINF_SUCCESS;
1165 }
1166
1167 pThis->Read.hActiveThread = NIL_RTTHREAD;
1168 }
1169 else
1170 rc = VERR_WRONG_ORDER;
1171 rtLocalIpcSessionReleaseAndUnlock(pThis);
1172 }
1173
1174 return rc;
1175}
1176
1177
1178RTDECL(int) RTLocalIpcSessionReadNB(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
1179{
1180 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1181 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1182 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1183 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1184 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
1185 *pcbRead = 0;
1186
1187 int rc = RTCritSectEnter(&pThis->CritSect);
1188 if (RT_SUCCESS(rc))
1189 {
1190 rtLocalIpcSessionRetain(pThis);
1191 if (pThis->Read.hActiveThread == NIL_RTTHREAD)
1192 {
1193 pThis->Read.hActiveThread = RTThreadSelf();
1194
1195 for (;;)
1196 {
1197 DWORD cbRead = 0;
1198 if (!pThis->fCancelled)
1199 {
1200 /*
1201 * Wait for pending zero byte read, if necessary.
1202 * Note! It cannot easily be cancelled due to concurrent current writes.
1203 */
1204 if (!pThis->fZeroByteRead)
1205 { /* likely */ }
1206 else
1207 {
1208 RTCritSectLeave(&pThis->CritSect);
1209 DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, 0);
1210 RTCritSectEnter(&pThis->CritSect);
1211
1212 rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
1213 if (RT_SUCCESS(rc))
1214 continue;
1215
1216 if (rc == VERR_TIMEOUT)
1217 rc = VINF_TRY_AGAIN;
1218 break;
1219 }
1220
1221 /*
1222 * Figure out how much we can read (cannot try and cancel here
1223 * like in the anonymous pipe code).
1224 */
1225 DWORD cbAvailable;
1226 if (PeekNamedPipe(pThis->hNmPipe, NULL, 0, NULL, &cbAvailable, NULL))
1227 {
1228 if (cbAvailable == 0 || cbToRead == 0)
1229 {
1230 *pcbRead = 0;
1231 rc = VINF_TRY_AGAIN;
1232 break;
1233 }
1234 }
1235 else
1236 {
1237 rc = RTErrConvertFromWin32(GetLastError());
1238 break;
1239 }
1240 if (cbAvailable > cbToRead)
1241 cbAvailable = (DWORD)cbToRead;
1242
1243 /*
1244 * Kick of a an overlapped read. It should return immediately, so we
1245 * don't really need to leave the critsect here.
1246 */
1247 rc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(rc == TRUE);
1248 if (ReadFile(pThis->hNmPipe, pvBuf, cbAvailable, &cbRead, &pThis->Read.OverlappedIO))
1249 {
1250 *pcbRead = cbRead;
1251 rc = VINF_SUCCESS;
1252 }
1253 else if (GetLastError() == ERROR_IO_PENDING)
1254 {
1255 DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, 0);
1256 if (rcWait == WAIT_TIMEOUT)
1257 {
1258 RTCritSectLeave(&pThis->CritSect);
1259 rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, INFINITE);
1260 RTCritSectEnter(&pThis->CritSect);
1261 }
1262 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, TRUE /*fWait*/))
1263 {
1264 *pcbRead = cbRead;
1265 rc = VINF_SUCCESS;
1266 }
1267 else
1268 {
1269 if (pThis->fCancelled)
1270 rc = VERR_CANCELLED;
1271 else
1272 rc = RTErrConvertFromWin32(GetLastError());
1273 }
1274 }
1275 else
1276 {
1277 rc = RTErrConvertFromWin32(GetLastError());
1278 AssertMsgFailedBreak(("%Rrc\n", rc));
1279 }
1280 }
1281 else
1282 rc = VERR_CANCELLED;
1283 break;
1284 }
1285
1286 pThis->Read.hActiveThread = NIL_RTTHREAD;
1287 }
1288 else
1289 rc = VERR_WRONG_ORDER;
1290 rtLocalIpcSessionReleaseAndUnlock(pThis);
1291 }
1292
1293 return rc;
1294}
1295
1296
1297#if 0 /* Non-blocking writes are not yet supported. */
1298/**
1299 * Common worker for handling I/O completion.
1300 *
1301 * This is used by RTLocalIpcSessionClose and RTLocalIpcSessionWrite.
1302 *
1303 * @returns IPRT status code.
1304 * @param pThis The pipe instance handle.
1305 */
1306static int rtLocalIpcSessionWriteCheckCompletion(PRTLOCALIPCSESSIONINT pThis)
1307{
1308 int rc;
1309 DWORD rcWait = WaitForSingleObject(pThis->OverlappedIO.hEvent, 0);
1310 if (rcWait == WAIT_OBJECT_0)
1311 {
1312 DWORD cbWritten = 0;
1313 if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &cbWritten, TRUE))
1314 {
1315 for (;;)
1316 {
1317 if (cbWritten >= pThis->cbBounceBufUsed)
1318 {
1319 pThis->fIOPending = false;
1320 rc = VINF_SUCCESS;
1321 break;
1322 }
1323
1324 /* resubmit the remainder of the buffer - can this actually happen? */
1325 memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
1326 rc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(rc == TRUE);
1327 if (!WriteFile(pThis->hNmPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
1328 &cbWritten, &pThis->OverlappedIO))
1329 {
1330 DWORD dwErr = GetLastError();
1331 if (dwErr == ERROR_IO_PENDING)
1332 rc = VINF_TRY_AGAIN;
1333 else
1334 {
1335 pThis->fIOPending = false;
1336 if (dwErr == ERROR_NO_DATA)
1337 rc = VERR_BROKEN_PIPE;
1338 else
1339 rc = RTErrConvertFromWin32(dwErr);
1340 }
1341 break;
1342 }
1343 Assert(cbWritten > 0);
1344 }
1345 }
1346 else
1347 {
1348 pThis->fIOPending = false;
1349 rc = RTErrConvertFromWin32(GetLastError());
1350 }
1351 }
1352 else if (rcWait == WAIT_TIMEOUT)
1353 rc = VINF_TRY_AGAIN;
1354 else
1355 {
1356 pThis->fIOPending = false;
1357 if (rcWait == WAIT_ABANDONED)
1358 rc = VERR_INVALID_HANDLE;
1359 else
1360 rc = RTErrConvertFromWin32(GetLastError());
1361 }
1362 return rc;
1363}
1364#endif
1365
1366
1367RTDECL(int) RTLocalIpcSessionWrite(RTLOCALIPCSESSION hSession, const void *pvBuf, size_t cbToWrite)
1368{
1369 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1370 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1371 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1372 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1373 AssertReturn(cbToWrite, VERR_INVALID_PARAMETER);
1374
1375 int rc = RTCritSectEnter(&pThis->CritSect);
1376 if (RT_SUCCESS(rc))
1377 {
1378 rtLocalIpcSessionRetain(pThis);
1379 if (pThis->Write.hActiveThread == NIL_RTTHREAD)
1380 {
1381 pThis->Write.hActiveThread = RTThreadSelf();
1382
1383 /*
1384 * Try write everything. No bounce buffering necessary.
1385 */
1386 size_t cbTotalWritten = 0;
1387 while (cbToWrite > 0)
1388 {
1389 DWORD cbWritten = 0;
1390 if (!pThis->fCancelled)
1391 {
1392 BOOL fRc = ResetEvent(pThis->Write.OverlappedIO.hEvent); Assert(fRc == TRUE);
1393 RTCritSectLeave(&pThis->CritSect);
1394
1395 DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0;
1396 fRc = WriteFile(pThis->hNmPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Write.OverlappedIO);
1397 if (fRc)
1398 rc = VINF_SUCCESS;
1399 else
1400 {
1401 DWORD dwErr = GetLastError();
1402 if (dwErr == ERROR_IO_PENDING)
1403 {
1404 DWORD rcWait = WaitForSingleObject(pThis->Write.OverlappedIO.hEvent, INFINITE);
1405 if (rcWait == WAIT_OBJECT_0)
1406 {
1407 if (GetOverlappedResult(pThis->hNmPipe, &pThis->Write.OverlappedIO, &cbWritten, TRUE /*fWait*/))
1408 rc = VINF_SUCCESS;
1409 else
1410 rc = RTErrConvertFromWin32(GetLastError());
1411 }
1412 else if (rcWait == WAIT_TIMEOUT)
1413 rc = VERR_TIMEOUT;
1414 else if (rcWait == WAIT_ABANDONED)
1415 rc = VERR_INVALID_HANDLE;
1416 else
1417 rc = RTErrConvertFromWin32(GetLastError());
1418 }
1419 else if (dwErr == ERROR_NO_DATA)
1420 rc = VERR_BROKEN_PIPE;
1421 else
1422 rc = RTErrConvertFromWin32(dwErr);
1423 }
1424
1425 if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */
1426 cbWritten = cbToWriteInThisIteration;
1427
1428 RTCritSectEnter(&pThis->CritSect);
1429 if (RT_FAILURE(rc))
1430 break;
1431 }
1432 else
1433 {
1434 rc = VERR_CANCELLED;
1435 break;
1436 }
1437
1438 /* Advance. */
1439 pvBuf = (char const *)pvBuf + cbWritten;
1440 cbTotalWritten += cbWritten;
1441 cbToWrite -= cbWritten;
1442 }
1443
1444 pThis->Write.hActiveThread = NIL_RTTHREAD;
1445 }
1446 else
1447 rc = VERR_WRONG_ORDER;
1448 rtLocalIpcSessionReleaseAndUnlock(pThis);
1449 }
1450
1451 return rc;
1452}
1453
1454
1455RTDECL(int) RTLocalIpcSessionFlush(RTLOCALIPCSESSION hSession)
1456{
1457 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1458 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1459 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1460
1461 int rc = RTCritSectEnter(&pThis->CritSect);
1462 if (RT_SUCCESS(rc))
1463 {
1464 if (pThis->Write.hActiveThread == NIL_RTTHREAD)
1465 {
1466 /* No flushing on Windows needed since RTLocalIpcSessionWrite will block until
1467 * all data was written (or an error occurred). */
1468 /** @todo r=bird: above comment is misinformed.
1469 * Implement this as soon as we want an explicit asynchronous version of
1470 * RTLocalIpcSessionWrite on Windows. */
1471 rc = VINF_SUCCESS;
1472 }
1473 else
1474 rc = VERR_WRONG_ORDER;
1475 RTCritSectLeave(&pThis->CritSect);
1476 }
1477 return rc;
1478}
1479
1480
1481RTDECL(int) RTLocalIpcSessionWaitForData(RTLOCALIPCSESSION hSession, uint32_t cMillies)
1482{
1483 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1484 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1485 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1486
1487 uint64_t const msStart = RTTimeMilliTS();
1488
1489 int rc = RTCritSectEnter(&pThis->CritSect);
1490 if (RT_SUCCESS(rc))
1491 {
1492 rtLocalIpcSessionRetain(pThis);
1493 if (pThis->Read.hActiveThread == NIL_RTTHREAD)
1494 {
1495 pThis->Read.hActiveThread = RTThreadSelf();
1496
1497 /*
1498 * Wait loop.
1499 */
1500 for (unsigned iLoop = 0;; iLoop++)
1501 {
1502 /*
1503 * Check for cancellation before we continue.
1504 */
1505 if (!pThis->fCancelled)
1506 { /* likely */ }
1507 else
1508 {
1509 rc = VERR_CANCELLED;
1510 break;
1511 }
1512
1513 /*
1514 * Prep something we can wait on.
1515 */
1516 HANDLE hWait = INVALID_HANDLE_VALUE;
1517 if (pThis->fZeroByteRead)
1518 hWait = pThis->Read.OverlappedIO.hEvent;
1519 else
1520 {
1521 /* Peek at the pipe buffer and see how many bytes it contains. */
1522 DWORD cbAvailable;
1523 if ( PeekNamedPipe(pThis->hNmPipe, NULL, 0, NULL, &cbAvailable, NULL)
1524 && cbAvailable)
1525 {
1526 rc = VINF_SUCCESS;
1527 break;
1528 }
1529
1530 /* Start a zero byte read operation that we can wait on. */
1531 if (cMillies == 0)
1532 {
1533 rc = VERR_TIMEOUT;
1534 break;
1535 }
1536 BOOL fRc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(fRc == TRUE); NOREF(fRc);
1537 DWORD cbRead = 0;
1538 if (ReadFile(pThis->hNmPipe, pThis->abBuf, 0 /*cbToRead*/, &cbRead, &pThis->Read.OverlappedIO))
1539 {
1540 rc = VINF_SUCCESS;
1541 if (iLoop > 10)
1542 RTThreadYield();
1543 }
1544 else if (GetLastError() == ERROR_IO_PENDING)
1545 {
1546 pThis->fZeroByteRead = true;
1547 hWait = pThis->Read.OverlappedIO.hEvent;
1548 }
1549 else
1550 rc = RTErrConvertFromWin32(GetLastError());
1551 if (RT_FAILURE(rc))
1552 break;
1553 }
1554
1555 /*
1556 * Check for timeout.
1557 */
1558 DWORD cMsMaxWait = INFINITE; /* (MSC maybe used uninitialized) */
1559 if (cMillies == RT_INDEFINITE_WAIT)
1560 cMsMaxWait = INFINITE;
1561 else if ( hWait != INVALID_HANDLE_VALUE
1562 || iLoop > 10)
1563 {
1564 uint64_t cMsElapsed = RTTimeMilliTS() - msStart;
1565 if (cMsElapsed <= cMillies)
1566 cMsMaxWait = cMillies - (uint32_t)cMsElapsed;
1567 else if (iLoop == 0)
1568 cMsMaxWait = cMillies ? 1 : 0;
1569 else
1570 {
1571 rc = VERR_TIMEOUT;
1572 break;
1573 }
1574 }
1575
1576 /*
1577 * Wait and collect the result.
1578 */
1579 if (hWait != INVALID_HANDLE_VALUE)
1580 {
1581 RTCritSectLeave(&pThis->CritSect);
1582
1583 DWORD rcWait = WaitForSingleObject(hWait, cMsMaxWait);
1584
1585 int rc2 = RTCritSectEnter(&pThis->CritSect);
1586 AssertRC(rc2);
1587
1588 rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
1589 break;
1590 }
1591 }
1592
1593 pThis->Read.hActiveThread = NIL_RTTHREAD;
1594 }
1595
1596 rtLocalIpcSessionReleaseAndUnlock(pThis);
1597 }
1598
1599 return rc;
1600}
1601
1602
1603RTDECL(int) RTLocalIpcSessionCancel(RTLOCALIPCSESSION hSession)
1604{
1605 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
1606 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1607 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1608
1609 /*
1610 * Enter the critical section, then set the cancellation flag
1611 * and signal the event (to wake up anyone in/at WaitForSingleObject).
1612 */
1613 int rc = RTCritSectEnter(&pThis->CritSect);
1614 if (RT_SUCCESS(rc))
1615 {
1616 rtLocalIpcSessionRetain(pThis);
1617 rc = rtLocalIpcWinCancel(pThis);
1618 rtLocalIpcSessionReleaseAndUnlock(pThis);
1619 }
1620
1621 return rc;
1622}
1623
1624
1625RTDECL(int) RTLocalIpcSessionQueryProcess(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess)
1626{
1627 RT_NOREF_PV(hSession); RT_NOREF_PV(pProcess);
1628 return VERR_NOT_SUPPORTED;
1629}
1630
1631
1632RTDECL(int) RTLocalIpcSessionQueryUserId(RTLOCALIPCSESSION hSession, PRTUID pUid)
1633{
1634 RT_NOREF_PV(hSession); RT_NOREF_PV(pUid);
1635 return VERR_NOT_SUPPORTED;
1636}
1637
1638
1639RTDECL(int) RTLocalIpcSessionQueryGroupId(RTLOCALIPCSESSION hSession, PRTGID pGid)
1640{
1641 RT_NOREF_PV(hSession); RT_NOREF_PV(pGid);
1642 return VERR_NOT_SUPPORTED;
1643}
1644
Note: See TracBrowser for help on using the repository browser.

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