VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 7 weeks ago

Copyright year updates by scm.

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