VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use