VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/shmem-posix.cpp@ 96108

Last change on this file since 96108 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.4 KB
Line 
1/* $Id: shmem-posix.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * IPRT - Named shared memory object, POSIX Implementation.
4 */
5
6/*
7 * Copyright (C) 2018-2022 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/shmem.h>
32#include "internal/iprt.h"
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/cdefs.h>
37#include <iprt/err.h>
38#include <iprt/mem.h>
39#include <iprt/string.h>
40#include "internal/magics.h"
41
42#include <errno.h>
43#include <sys/mman.h>
44#include <sys/stat.h>
45#include <fcntl.h>
46#include <unistd.h>
47#include <limits.h>
48
49/* Workaround on systems which do not provide this. */
50#ifndef NAME_MAX
51# define NAME_MAX 255
52#endif
53
54
55/*********************************************************************************************************************************
56* Structures and Typedefs *
57*********************************************************************************************************************************/
58
59/**
60 * Shared memory object mapping descriptor.
61 */
62typedef struct RTSHMEMMAPPINGDESC
63{
64 /** Number of references held to this mapping, 0 if the descriptor is free. */
65 volatile uint32_t cMappings;
66 /** Pointer to the region mapping. */
67 void *pvMapping;
68 /** Start offset */
69 size_t offRegion;
70 /** Size of the region. */
71 size_t cbRegion;
72 /** Access flags for this region .*/
73 uint32_t fFlags;
74} RTSHMEMMAPPINGDESC;
75/** Pointer to a shared memory object mapping descriptor. */
76typedef RTSHMEMMAPPINGDESC *PRTSHMEMMAPPINGDESC;
77/** Pointer to a constant shared memory object mapping descriptor. */
78typedef const RTSHMEMMAPPINGDESC *PCRTSHMEMMAPPINGDESC;
79
80
81/**
82 * Internal shared memory object state.
83 */
84typedef struct RTSHMEMINT
85{
86 /** Magic value (RTSHMEM_MAGIC). */
87 uint32_t u32Magic;
88 /** File descriptor for the underlying shared memory object. */
89 int iFdShm;
90 /** Pointer to the shared memory object name. */
91 char *pszName;
92 /** Flag whether this instance created the named shared memory object. */
93 bool fCreate;
94 /** Overall number of mappings active for this shared memory object. */
95 volatile uint32_t cMappings;
96 /** Maximum number of mapping descriptors allocated. */
97 uint32_t cMappingDescsMax;
98 /** Number of mapping descriptors used. */
99 volatile uint32_t cMappingDescsUsed;
100 /** Array of mapping descriptors - variable in size. */
101 RTSHMEMMAPPINGDESC aMappingDescs[1];
102} RTSHMEMINT;
103/** Pointer to the internal shared memory object state. */
104typedef RTSHMEMINT *PRTSHMEMINT;
105
106
107
108/**
109 * Returns a mapping descriptor matching the given region properties or NULL if none was found.
110 *
111 * @returns Pointer to the matching mapping descriptor or NULL if not found.
112 * @param pThis Pointer to the shared memory object instance.
113 * @param offRegion Offset into the shared memory object to start mapping at.
114 * @param cbRegion Size of the region to map.
115 * @param fFlags Desired properties of the mapped region, combination of RTSHMEM_MAP_F_* defines.
116 */
117DECLINLINE(PRTSHMEMMAPPINGDESC) rtShMemMappingDescFindByProp(PRTSHMEMINT pThis, size_t offRegion, size_t cbRegion, uint32_t fFlags)
118{
119 for (uint32_t i = 0; i < pThis->cMappingDescsMax; i++)
120 {
121 if ( pThis->aMappingDescs[i].offRegion == offRegion
122 && pThis->aMappingDescs[i].cbRegion == cbRegion
123 && pThis->aMappingDescs[i].fFlags == fFlags)
124 return &pThis->aMappingDescs[i];
125 }
126
127 return NULL;
128}
129
130
131RTDECL(int) RTShMemOpen(PRTSHMEM phShMem, const char *pszName, uint32_t fFlags, size_t cbMax, uint32_t cMappingsHint)
132{
133 AssertPtrReturn(phShMem, VERR_INVALID_PARAMETER);
134 AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
135 AssertReturn(!(fFlags & ~RTSHMEM_O_F_VALID_MASK), VERR_INVALID_PARAMETER);
136 AssertReturn(cMappingsHint < 64, VERR_OUT_OF_RANGE);
137
138 size_t cchName = strlen(pszName);
139 AssertReturn(cchName, VERR_INVALID_PARAMETER);
140 AssertReturn(cchName < NAME_MAX - 1, VERR_INVALID_PARAMETER); /* account for the / we add later on. */
141 cMappingsHint = cMappingsHint == 0 ? 5 : cMappingsHint;
142 int rc = VINF_SUCCESS;
143 PRTSHMEMINT pThis = (PRTSHMEMINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTSHMEMINT, aMappingDescs[cMappingsHint]) + cchName + 2); /* '/' + terminator. */
144 if (RT_LIKELY(pThis))
145 {
146 pThis->u32Magic = RTSHMEM_MAGIC;
147 pThis->pszName = (char *)&pThis->aMappingDescs[cMappingsHint];
148 /*pThis->fCreate = false; */
149 /*pThis->cMappings = 0; */
150 pThis->cMappingDescsMax = cMappingsHint;
151 /*pThis->cMappingDescsUsed = 0; */
152 pThis->pszName[0] = '/';
153 memcpy(&pThis->pszName[1], pszName, cchName);
154 int fShmFlags = 0;
155 if (fFlags & RTSHMEM_O_F_CREATE)
156 {
157 fShmFlags |= O_CREAT;
158 pThis->fCreate = true;
159 }
160 if ((fFlags & RTSHMEM_O_F_CREATE_EXCL) == RTSHMEM_O_F_CREATE_EXCL)
161 fShmFlags |= O_EXCL;
162 if ( (fFlags & RTSHMEM_O_F_READWRITE) == RTSHMEM_O_F_READWRITE
163 || (fFlags & RTSHMEM_O_F_WRITE))
164 fShmFlags |= O_RDWR;
165 else
166 fShmFlags |= O_RDONLY;
167 if (fFlags & RTSHMEM_O_F_TRUNCATE)
168 fShmFlags |= O_TRUNC;
169 pThis->iFdShm = shm_open(pThis->pszName, fShmFlags , 0600);
170 if (pThis->iFdShm > 0)
171 {
172 if (cbMax)
173 rc = RTShMemSetSize(pThis, cbMax);
174 if (RT_SUCCESS(rc))
175 {
176 *phShMem = pThis;
177 return VINF_SUCCESS;
178 }
179
180 close(pThis->iFdShm);
181 }
182 else
183 rc = RTErrConvertFromErrno(errno);
184
185 RTMemFree(pThis);
186 }
187 else
188 rc = VERR_NO_MEMORY;
189
190 return rc;
191}
192
193
194RTDECL(int) RTShMemClose(RTSHMEM hShMem)
195{
196 PRTSHMEMINT pThis = hShMem;
197 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
198 AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
199 AssertReturn(!pThis->cMappings, VERR_INVALID_STATE);
200
201 int rc = VINF_SUCCESS;
202 if (!close(pThis->iFdShm))
203 {
204 if (pThis->fCreate)
205 shm_unlink(pThis->pszName); /* Ignore any error here. */
206 pThis->u32Magic = RTSHMEM_MAGIC_DEAD;
207 RTMemFree(pThis);
208 }
209 else
210 rc = RTErrConvertFromErrno(errno);
211
212 return rc;
213}
214
215
216RTDECL(int) RTShMemDelete(const char *pszName)
217{
218 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
219
220 size_t cchName = strlen(pszName);
221 AssertReturn(cchName, VERR_INVALID_PARAMETER);
222 AssertReturn(cchName < NAME_MAX - 1, VERR_INVALID_PARAMETER); /* account for the / we add later on. */
223 char *psz = NULL;
224
225 int rc = RTStrAllocEx(&psz, cchName + 2); /* '/' + terminator */
226 if (RT_SUCCESS(rc))
227 {
228 psz[0] = '/';
229 memcpy(&psz[1], pszName, cchName + 1);
230 if (shm_unlink(psz))
231 rc = RTErrConvertFromErrno(errno);
232 RTStrFree(psz);
233 }
234
235 return rc;
236}
237
238
239RTDECL(uint32_t) RTShMemRefCount(RTSHMEM hShMem)
240{
241 PRTSHMEMINT pThis = hShMem;
242 AssertPtrReturn(pThis, 0);
243 AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, 0);
244
245 return pThis->cMappings;
246}
247
248
249RTDECL(int) RTShMemSetSize(RTSHMEM hShMem, size_t cbMem)
250{
251 PRTSHMEMINT pThis = hShMem;
252 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
253 AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
254 AssertReturn(!pThis->cMappings, VERR_INVALID_STATE);
255
256 int rc = VINF_SUCCESS;
257 if (ftruncate(pThis->iFdShm, (off_t)cbMem))
258 rc = RTErrConvertFromErrno(errno);
259
260 return rc;
261}
262
263
264RTDECL(int) RTShMemQuerySize(RTSHMEM hShMem, size_t *pcbMem)
265{
266 PRTSHMEMINT pThis = hShMem;
267 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
268 AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
269 AssertPtrReturn(pcbMem, VERR_INVALID_PARAMETER);
270
271 struct stat st;
272 if (!fstat(pThis->iFdShm, &st))
273 {
274 *pcbMem = st.st_size;
275 return VINF_SUCCESS;
276 }
277 return RTErrConvertFromErrno(errno);
278}
279
280
281RTDECL(int) RTShMemMapRegion(RTSHMEM hShMem, size_t offRegion, size_t cbRegion, uint32_t fFlags, void **ppv)
282{
283 PRTSHMEMINT pThis = hShMem;
284 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
285 AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
286 AssertPtrReturn(ppv, VERR_INVALID_PARAMETER);
287 AssertReturn(!(fFlags & ~RTSHMEM_MAP_F_VALID_MASK), VERR_INVALID_PARAMETER);
288
289 /* Try to find a mapping with compatible parameters first. */
290 PRTSHMEMMAPPINGDESC pMappingDesc = NULL;
291 for (uint32_t iTry = 0; iTry < 10; iTry++)
292 {
293 pMappingDesc = rtShMemMappingDescFindByProp(pThis, offRegion, cbRegion, fFlags);
294 if (!pMappingDesc)
295 break;
296
297 /* Increase the mapping count and check that the region is still accessible by us. */
298 if ( ASMAtomicIncU32(&pMappingDesc->cMappings) > 1
299 && pMappingDesc->offRegion == offRegion
300 && pMappingDesc->cbRegion == cbRegion
301 && pMappingDesc->fFlags == fFlags)
302 break;
303 /* Mapping was freed inbetween, next round. */
304 }
305
306 int rc = VINF_SUCCESS;
307 if (!pMappingDesc)
308 {
309 /* Find an empty region descriptor and map the region. */
310 for (uint32_t i = 0; i < pThis->cMappingDescsMax && !pMappingDesc; i++)
311 {
312 if (!pThis->aMappingDescs[i].cMappings)
313 {
314 pMappingDesc = &pThis->aMappingDescs[i];
315
316 /* Try to grab this one. */
317 if (ASMAtomicIncU32(&pMappingDesc->cMappings) == 1)
318 break;
319
320 /* Somebody raced us, drop reference and continue. */
321 ASMAtomicDecU32(&pMappingDesc->cMappings);
322 pMappingDesc = NULL;
323 }
324 }
325
326 if (RT_LIKELY(pMappingDesc))
327 {
328 /* Try to map it. */
329 int fMmapFlags = 0;
330 int fProt = 0;
331 if (fFlags & RTSHMEM_MAP_F_READ)
332 fProt |= PROT_READ;
333 if (fFlags & RTSHMEM_MAP_F_WRITE)
334 fProt |= PROT_WRITE;
335 if (fFlags & RTSHMEM_MAP_F_EXEC)
336 fProt |= PROT_EXEC;
337 if (fFlags & RTSHMEM_MAP_F_COW)
338 fMmapFlags |= MAP_PRIVATE;
339 else
340 fMmapFlags |= MAP_SHARED;
341
342 void *pv = mmap(NULL, cbRegion, fProt, fMmapFlags, pThis->iFdShm, (off_t)offRegion);
343 if (pv != MAP_FAILED)
344 {
345 pMappingDesc->pvMapping = pv;
346 pMappingDesc->offRegion = offRegion;
347 pMappingDesc->cbRegion = cbRegion;
348 pMappingDesc->fFlags = fFlags;
349 }
350 else
351 {
352 rc = RTErrConvertFromErrno(errno);
353 ASMAtomicDecU32(&pMappingDesc->cMappings);
354 }
355 }
356 else
357 rc = VERR_SHMEM_MAXIMUM_MAPPINGS_REACHED;
358 }
359
360 if (RT_SUCCESS(rc))
361 {
362 *ppv = pMappingDesc->pvMapping;
363 ASMAtomicIncU32(&pThis->cMappings);
364 }
365
366 return rc;
367}
368
369
370RTDECL(int) RTShMemUnmapRegion(RTSHMEM hShMem, void *pv)
371{
372 PRTSHMEMINT pThis = hShMem;
373 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
374 AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
375 AssertPtrReturn(pv, VERR_INVALID_PARAMETER);
376
377 /* Find the mapping descriptor by the given region address. */
378 PRTSHMEMMAPPINGDESC pMappingDesc = NULL;
379 for (uint32_t i = 0; i < pThis->cMappingDescsMax && !pMappingDesc; i++)
380 {
381 if (pThis->aMappingDescs[i].pvMapping == pv)
382 {
383 pMappingDesc = &pThis->aMappingDescs[i];
384 break;
385 }
386 }
387
388 AssertPtrReturn(pMappingDesc, VERR_INVALID_PARAMETER);
389
390 int rc = VINF_SUCCESS;
391 size_t cbRegion = pMappingDesc->cMappings;
392 if (!ASMAtomicDecU32(&pMappingDesc->cMappings))
393 {
394 /* Last mapping of this region was unmapped, so do the real unmapping now. */
395 if (munmap(pv, cbRegion))
396 {
397 ASMAtomicIncU32(&pMappingDesc->cMappings);
398 rc = RTErrConvertFromErrno(errno);
399 }
400 else
401 {
402 ASMAtomicDecU32(&pThis->cMappingDescsUsed);
403 ASMAtomicDecU32(&pThis->cMappings);
404 }
405 }
406
407 return rc;
408}
409
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