VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/shmem-win.cpp@ 75879

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

Runtime: Introduce RTShMem* API for accessing shared memory objects with implementations for POSIX (via shm_*) and Windows (via CreateFileMapping)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.7 KB
Line 
1/* $Id: shmem-win.cpp 75879 2018-12-02 18:39:16Z vboxsync $ */
2/** @file
3 * IPRT - Named shared memory object, Windows Implementation.
4 */
5
6/*
7 * Copyright (C) 2018 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#include <iprt/nt/nt-and-windows.h>
32
33#include <iprt/shmem.h>
34#include "internal/iprt.h"
35
36#include <iprt/asm.h>
37#include <iprt/assert.h>
38#include <iprt/cdefs.h>
39#include <iprt/err.h>
40#include <iprt/mem.h>
41#include <iprt/string.h>
42#include "internal/magics.h"
43#include "internal/path.h"
44#include "internal-r3-win.h" /* For g_enmWinVer + kRTWinOSType_XXX */
45
46/*
47 * Define values ourselves in case the compiling host is too old.
48 * See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createfilemappinga
49 * for when these were introduced.
50 */
51#ifndef PAGE_EXECUTE_READ
52# define PAGE_EXECUTE_READ 0x20
53#endif
54#ifndef PAGE_EXECUTE_READWRITE
55# define PAGE_EXECUTE_READWRITE 0x40
56#endif
57#ifndef PAGE_EXECUTE_WRITECOPY
58# define PAGE_EXECUTE_WRITECOPY 0x80
59#endif
60#ifndef FILE_MAP_EXECUTE
61# define FILE_MAP_EXECUTE 0x20
62#endif
63
64/*********************************************************************************************************************************
65* Structures and Typedefs *
66*********************************************************************************************************************************/
67
68/**
69 * Shared memory object mapping descriptor.
70 */
71typedef struct RTSHMEMMAPPINGDESC
72{
73 /** Number of references held to this mapping, 0 if the descriptor is free. */
74 volatile uint32_t cMappings;
75 /** Pointer to the region mapping. */
76 void *pvMapping;
77 /** Start offset */
78 size_t offRegion;
79 /** Size of the region. */
80 size_t cbRegion;
81 /** Access flags for this region .*/
82 uint32_t fFlags;
83} RTSHMEMMAPPINGDESC;
84/** Pointer to a shared memory object mapping descriptor. */
85typedef RTSHMEMMAPPINGDESC *PRTSHMEMMAPPINGDESC;
86/** Pointer to a constant shared memory object mapping descriptor. */
87typedef const RTSHMEMMAPPINGDESC *PCRTSHMEMMAPPINGDESC;
88
89
90/**
91 * Internal shared memory object state.
92 */
93typedef struct RTSHMEMINT
94{
95 /** Magic value (RTSHMEM_MAGIC). */
96 uint32_t u32Magic;
97 /** Handle to the underlying mapping object. */
98 HANDLE hShmObj;
99 /** Flag whether this instance created the named shared memory object. */
100 bool fCreate;
101 /** Size of the mapping object in bytes. */
102 size_t cbMax;
103 /** Overall number of mappings active for this shared memory object. */
104 volatile uint32_t cMappings;
105 /** Maximum number of mapping descriptors allocated. */
106 uint32_t cMappingDescsMax;
107 /** Number of mapping descriptors used. */
108 volatile uint32_t cMappingDescsUsed;
109 /** Array of mapping descriptors - variable in size. */
110 RTSHMEMMAPPINGDESC aMappingDescs[1];
111} RTSHMEMINT;
112/** Pointer to the internal shared memory object state. */
113typedef RTSHMEMINT *PRTSHMEMINT;
114
115
116/*********************************************************************************************************************************
117* Internal Functions *
118*********************************************************************************************************************************/
119
120
121/**
122 * Returns a mapping descriptor matching the given region properties or NULL if none was found.
123 *
124 * @returns Pointer to the matching mapping descriptor or NULL if not found.
125 * @param pThis Pointer to the shared memory object instance.
126 * @param offRegion Offset into the shared memory object to start mapping at.
127 * @param cbRegion Size of the region to map.
128 * @param fFlags Desired properties of the mapped region, combination of RTSHMEM_MAP_F_* defines.
129 */
130DECLINLINE(PRTSHMEMMAPPINGDESC) rtShMemMappingDescFindByProp(PRTSHMEMINT pThis, size_t offRegion, size_t cbRegion, uint32_t fFlags)
131{
132 for (uint32_t i = 0; i < pThis->cMappingDescsMax; i++)
133 {
134 if ( pThis->aMappingDescs[i].offRegion == offRegion
135 && pThis->aMappingDescs[i].cbRegion == cbRegion
136 && pThis->aMappingDescs[i].fFlags == fFlags)
137 return &pThis->aMappingDescs[i];
138 }
139
140 return NULL;
141}
142
143
144RTDECL(int) RTShMemOpen(PRTSHMEM phShMem, const char *pszName, uint32_t fFlags, size_t cbMax, uint32_t cMappingsHint)
145{
146 AssertPtrReturn(phShMem, VERR_INVALID_PARAMETER);
147 AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
148 AssertReturn(!(fFlags & ~RTSHMEM_O_F_VALID_MASK), VERR_INVALID_PARAMETER);
149 AssertReturn(cMappingsHint < 64, VERR_OUT_OF_RANGE);
150 AssertReturn(cbMax > 0 || !(fFlags & RTSHMEM_O_F_CREATE), VERR_NOT_SUPPORTED);
151
152 if (fFlags & RTSHMEM_O_F_TRUNCATE)
153 return VERR_NOT_SUPPORTED;
154
155 /*
156 * The executable access was introduced with Windows XP SP2 and Windows Server 2003 SP1,
157 * PAGE_EXECUTE_WRITECOPY was not available until Windows Vista SP1.
158 * Allow execute mappings only starting from Windows 7 to keep the checks simple here (lazy coder).
159 */
160 if ( (fFlags & RTSHMEM_O_F_MAYBE_EXEC)
161 && g_enmWinVer < kRTWinOSType_7)
162 return VERR_NOT_SUPPORTED;
163
164 cMappingsHint = cMappingsHint == 0 ? 5 : cMappingsHint;
165 int rc = VINF_SUCCESS;
166 PRTSHMEMINT pThis = (PRTSHMEMINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTSHMEMINT, aMappingDescs[cMappingsHint]));
167 if (RT_LIKELY(pThis))
168 {
169 pThis->u32Magic = RTSHMEM_MAGIC;
170 /*pThis->fCreate = false; */
171 /*pThis->cMappings = 0; */
172 pThis->cMappingDescsMax = cMappingsHint;
173 /*pThis->cMappingDescsUsed = 0; */
174 /* Construct the filename, always use the local namespace, global requires special privileges. */
175 char szName[RTPATH_MAX];
176 ssize_t cch = RTStrPrintf2(&szName[0], sizeof(szName), "Local\\%s", pszName);
177 if (cch > 0)
178 {
179 PRTUTF16 pwszName = NULL;
180 rc = RTStrToUtf16Ex(&szName[0], RTSTR_MAX, &pwszName, 0, NULL);
181 if (RT_SUCCESS(rc))
182 {
183 if (fFlags & RTSHMEM_O_F_CREATE)
184 {
185#if HC_ARCH_BITS == 64
186 DWORD dwSzMaxHigh = cbMax >> 32;
187#elif HC_ARCH_BITS == 32
188 DWORD dwSzMaxHigh = 0;
189#else
190# error "Port me"
191#endif
192 DWORD dwSzMaxLow = cbMax & UINT32_C(0xffffffff);
193 DWORD fProt = 0;
194
195 if (fFlags & RTSHMEM_O_F_MAYBE_EXEC)
196 {
197 if ((fFlags & RTSHMEM_O_F_READWRITE) == RTSHMEM_O_F_READ)
198 fProt |= PAGE_EXECUTE_READ;
199 else
200 fProt |= PAGE_EXECUTE_READWRITE;
201 }
202 else
203 {
204 if ((fFlags & RTSHMEM_O_F_READWRITE) == RTSHMEM_O_F_READ)
205 fProt |= PAGE_READONLY;
206 else
207 fProt |= PAGE_READWRITE;
208 }
209 pThis->hShmObj = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, fProt,
210 dwSzMaxHigh, dwSzMaxLow, pwszName);
211 }
212 else
213 {
214 DWORD fProt = SECTION_QUERY;
215 if (fFlags & RTSHMEM_O_F_MAYBE_EXEC)
216 fProt |= FILE_MAP_EXECUTE;
217 if (fFlags & RTSHMEM_O_F_READ)
218 fProt |= FILE_MAP_READ;
219 if (fFlags & RTSHMEM_O_F_WRITE)
220 fProt |= FILE_MAP_WRITE;
221
222 pThis->hShmObj = OpenFileMappingW(fProt, FALSE, pwszName);
223 }
224 RTUtf16Free(pwszName);
225 if (pThis->hShmObj != NULL)
226 {
227 *phShMem = pThis;
228 return VINF_SUCCESS;
229 }
230 else
231 rc = RTErrConvertFromWin32(GetLastError());
232 }
233 }
234 else
235 rc = VERR_BUFFER_OVERFLOW;
236
237 RTMemFree(pThis);
238 }
239 else
240 rc = VERR_NO_MEMORY;
241
242 return rc;
243}
244
245
246RTDECL(int) RTShMemClose(RTSHMEM hShMem)
247{
248 PRTSHMEMINT pThis = hShMem;
249 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
250 AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
251 AssertReturn(!pThis->cMappings, VERR_INVALID_STATE);
252
253 int rc = VINF_SUCCESS;
254 if (CloseHandle(pThis->hShmObj))
255 {
256 pThis->u32Magic = RTSHMEM_MAGIC_DEAD;
257 RTMemFree(pThis);
258 }
259 else
260 rc = RTErrConvertFromWin32(GetLastError());
261
262 return rc;
263}
264
265
266RTDECL(uint32_t) RTShMemRefCount(RTSHMEM hShMem)
267{
268 PRTSHMEMINT pThis = hShMem;
269 AssertPtrReturn(pThis, 0);
270 AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, 0);
271
272 return pThis->cMappings;
273}
274
275
276RTDECL(int) RTShMemSetSize(RTSHMEM hShMem, size_t cbMem)
277{
278 PRTSHMEMINT pThis = hShMem;
279 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
280 AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
281 AssertReturn(!pThis->cMappings, VERR_INVALID_STATE);
282 AssertReturn(cbMem, VERR_NOT_SUPPORTED);
283
284 return VERR_NOT_SUPPORTED;
285}
286
287
288RTDECL(int) RTShMemQuerySize(RTSHMEM hShMem, size_t *pcbMem)
289{
290 PRTSHMEMINT pThis = hShMem;
291 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
292 AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
293 AssertPtrReturn(pcbMem, VERR_INVALID_PARAMETER);
294
295 int rc = VINF_SUCCESS;
296 SECTION_BASIC_INFORMATION SecInf;
297 SIZE_T cbRet;
298 NTSTATUS rcNt = NtQuerySection(pThis->hShmObj, SectionBasicInformation, &SecInf, sizeof(SecInf), &cbRet);
299 if (NT_SUCCESS(rcNt))
300 {
301 AssertReturn(cbRet == sizeof(SecInf), VERR_INTERNAL_ERROR);
302#if HC_ARCH_BITS == 32
303 AssertReturn(SecInf.MaximumSize.HighPart == 0, VERR_INTERNAL_ERROR_2);
304 *pcbMem = SecInf.MaximumSize.LowPart;
305#elif HC_ARCH_BITS == 64
306 *pcbMem = SecInf.MaximumSize.QuadPart;
307#else
308# error "Port me"
309#endif
310 }
311 else
312 rc = RTErrConvertFromNtStatus(rcNt);
313
314 return rc;
315}
316
317
318RTDECL(int) RTShMemMapRegion(RTSHMEM hShMem, size_t offRegion, size_t cbRegion, uint32_t fFlags, void **ppv)
319{
320 PRTSHMEMINT pThis = hShMem;
321 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
322 AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
323 AssertPtrReturn(ppv, VERR_INVALID_PARAMETER);
324 AssertReturn(!(fFlags & ~RTSHMEM_MAP_F_VALID_MASK), VERR_INVALID_PARAMETER);
325
326 /* See comment in RTShMemOpen(). */
327 if ( (fFlags & RTSHMEM_MAP_F_EXEC)
328 && g_enmWinVer < kRTWinOSType_7)
329 return VERR_NOT_SUPPORTED;
330
331 /* Try to find a mapping with compatible parameters first. */
332 PRTSHMEMMAPPINGDESC pMappingDesc = NULL;
333 for (uint32_t iTry = 0; iTry < 10; iTry++)
334 {
335 pMappingDesc = rtShMemMappingDescFindByProp(pThis, offRegion, cbRegion, fFlags);
336 if (!pMappingDesc)
337 break;
338
339 /* Increase the mapping count and check that the region is still accessible by us. */
340 if ( ASMAtomicIncU32(&pMappingDesc->cMappings) > 1
341 && pMappingDesc->offRegion == offRegion
342 && pMappingDesc->cbRegion == cbRegion
343 && pMappingDesc->fFlags == fFlags)
344 break;
345 /* Mapping was freed inbetween, next round. */
346 }
347
348 int rc = VINF_SUCCESS;
349 if (!pMappingDesc)
350 {
351 /* Find an empty region descriptor and map the region. */
352 for (uint32_t i = 0; i < pThis->cMappingDescsMax && !pMappingDesc; i++)
353 {
354 if (!pThis->aMappingDescs[i].cMappings)
355 {
356 pMappingDesc = &pThis->aMappingDescs[i];
357
358 /* Try to grab this one. */
359 if (ASMAtomicIncU32(&pMappingDesc->cMappings) == 1)
360 break;
361
362 /* Somebody raced us, drop reference and continue. */
363 ASMAtomicDecU32(&pMappingDesc->cMappings);
364 pMappingDesc = NULL;
365 }
366 }
367
368 if (RT_LIKELY(pMappingDesc))
369 {
370 /* Try to map it. */
371 DWORD fProt = 0;
372 DWORD offLow = offRegion & UINT32_C(0xffffffff);
373#if HC_ARCH_BITS == 64
374 DWORD offHigh = offRegion >> 32;
375#elif HC_ARCH_BITS == 32
376 DWORD offHigh = 0;
377#else
378# error "Port me"
379#endif
380 if (fFlags & RTSHMEM_MAP_F_READ)
381 fProt |= FILE_MAP_READ;
382 if (fFlags & RTSHMEM_MAP_F_WRITE)
383 fProt |= FILE_MAP_WRITE;
384 if (fFlags & RTSHMEM_MAP_F_EXEC)
385 fProt |= FILE_MAP_EXECUTE;
386 if (fFlags & RTSHMEM_MAP_F_COW)
387 fProt |= FILE_MAP_COPY;
388
389 void *pv = MapViewOfFile(pThis->hShmObj, fProt, offHigh, offLow, cbRegion);
390 if (pv != NULL)
391 {
392 pMappingDesc->pvMapping = pv;
393 pMappingDesc->offRegion = offRegion;
394 pMappingDesc->cbRegion = cbRegion;
395 pMappingDesc->fFlags = fFlags;
396 }
397 else
398 {
399 rc = RTErrConvertFromWin32(GetLastError());
400 ASMAtomicDecU32(&pMappingDesc->cMappings);
401 }
402 }
403 else
404 rc = VERR_SHMEM_MAXIMUM_MAPPINGS_REACHED;
405 }
406
407 if (RT_SUCCESS(rc))
408 {
409 *ppv = pMappingDesc->pvMapping;
410 ASMAtomicIncU32(&pThis->cMappings);
411 }
412
413 return rc;
414}
415
416
417RTDECL(int) RTShMemUnmapRegion(RTSHMEM hShMem, void *pv)
418{
419 PRTSHMEMINT pThis = hShMem;
420 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
421 AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
422 AssertPtrReturn(pv, VERR_INVALID_PARAMETER);
423
424 /* Find the mapping descriptor by the given region address. */
425 PRTSHMEMMAPPINGDESC pMappingDesc = NULL;
426 for (uint32_t i = 0; i < pThis->cMappingDescsMax && !pMappingDesc; i++)
427 {
428 if (pThis->aMappingDescs[i].pvMapping == pv)
429 {
430 pMappingDesc = &pThis->aMappingDescs[i];
431 break;
432 }
433 }
434
435 AssertPtrReturn(pMappingDesc, VERR_INVALID_PARAMETER);
436
437 int rc = VINF_SUCCESS;
438 if (!ASMAtomicDecU32(&pMappingDesc->cMappings))
439 {
440 /* Last mapping of this region was unmapped, so do the real unmapping now. */
441 if (UnmapViewOfFile(pv))
442 {
443 ASMAtomicDecU32(&pThis->cMappingDescsUsed);
444 ASMAtomicDecU32(&pThis->cMappings);
445 }
446 else
447 {
448 ASMAtomicIncU32(&pMappingDesc->cMappings);
449 rc = RTErrConvertFromWin32(GetLastError());
450 }
451 }
452
453 return rc;
454}
455
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