]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
2d21ac55 | 2 | * Copyright (c) 2000-2007 Apple Inc. All rights reserved. |
5d5c5d0d | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
1c79356b | 5 | * |
2d21ac55 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
8f6c56a5 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b A |
27 | */ |
28 | /* | |
29 | * Copyright (c) 1990, 1996-1998 Apple Computer, Inc. | |
30 | * All Rights Reserved. | |
31 | */ | |
32 | /* | |
9bccf70c | 33 | * posix_shm.c : Support for POSIX shared memory APIs |
1c79356b A |
34 | * |
35 | * File: posix_shm.c | |
36 | * Author: Ananthakrishna Ramesh | |
37 | * | |
38 | * HISTORY | |
39 | * 2-Sep-1999 A.Ramesh | |
40 | * Created for MacOSX | |
41 | * | |
42 | */ | |
2d21ac55 A |
43 | /* |
44 | * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce | |
45 | * support for mandatory and extensible security protections. This notice | |
46 | * is included in support of clause 2.2 (b) of the Apple Public License, | |
47 | * Version 2.0. | |
48 | */ | |
1c79356b A |
49 | |
50 | #include <sys/cdefs.h> | |
51 | #include <sys/param.h> | |
52 | #include <sys/systm.h> | |
53 | #include <sys/kernel.h> | |
91447636 | 54 | #include <sys/file_internal.h> |
1c79356b A |
55 | #include <sys/filedesc.h> |
56 | #include <sys/stat.h> | |
91447636 A |
57 | #include <sys/proc_internal.h> |
58 | #include <sys/kauth.h> | |
1c79356b A |
59 | #include <sys/mount.h> |
60 | #include <sys/namei.h> | |
61 | #include <sys/vnode.h> | |
2d21ac55 | 62 | #include <sys/vnode_internal.h> |
1c79356b A |
63 | #include <sys/ioctl.h> |
64 | #include <sys/tty.h> | |
65 | #include <sys/malloc.h> | |
66 | #include <sys/mman.h> | |
91447636 A |
67 | #include <sys/stat.h> |
68 | #include <sys/sysproto.h> | |
0c530ab8 | 69 | #include <sys/proc_info.h> |
b0d623f7 | 70 | #include <security/audit/audit.h> |
e5568f75 | 71 | |
2d21ac55 A |
72 | #if CONFIG_MACF |
73 | #include <security/mac_framework.h> | |
74 | #endif | |
75 | ||
1c79356b | 76 | #include <mach/mach_types.h> |
91447636 A |
77 | #include <mach/mach_vm.h> |
78 | #include <mach/vm_map.h> | |
1c79356b A |
79 | #include <mach/vm_prot.h> |
80 | #include <mach/vm_inherit.h> | |
81 | #include <mach/kern_return.h> | |
82 | #include <mach/memory_object_control.h> | |
83 | ||
91447636 A |
84 | #include <vm/vm_map.h> |
85 | #include <vm/vm_protos.h> | |
1c79356b | 86 | |
91447636 | 87 | #define f_flag f_fglob->fg_flag |
39236c6e | 88 | #define f_type f_fglob->fg_ops->fo_type |
91447636 A |
89 | #define f_msgcount f_fglob->fg_msgcount |
90 | #define f_cred f_fglob->fg_cred | |
91 | #define f_ops f_fglob->fg_ops | |
92 | #define f_offset f_fglob->fg_offset | |
93 | #define f_data f_fglob->fg_data | |
1c79356b A |
94 | #define PSHMNAMLEN 31 /* maximum name segment length we bother with */ |
95 | ||
b0d623f7 A |
96 | struct pshmobj { |
97 | void * pshmo_memobject; | |
98 | memory_object_size_t pshmo_size; | |
99 | struct pshmobj * pshmo_next; | |
100 | }; | |
101 | ||
1c79356b A |
102 | struct pshminfo { |
103 | unsigned int pshm_flags; | |
104 | unsigned int pshm_usecount; | |
105 | off_t pshm_length; | |
106 | mode_t pshm_mode; | |
107 | uid_t pshm_uid; | |
108 | gid_t pshm_gid; | |
109 | char pshm_name[PSHMNAMLEN + 1]; /* segment name */ | |
b0d623f7 | 110 | struct pshmobj *pshm_memobjects; |
1c79356b A |
111 | #if DIAGNOSTIC |
112 | unsigned int pshm_readcount; | |
113 | unsigned int pshm_writecount; | |
2d21ac55 | 114 | proc_t pshm_proc; |
1c79356b | 115 | #endif /* DIAGNOSTIC */ |
2d21ac55 | 116 | struct label* pshm_label; |
1c79356b A |
117 | }; |
118 | #define PSHMINFO_NULL (struct pshminfo *)0 | |
119 | ||
b0d623f7 A |
120 | #define PSHM_NONE 0x001 |
121 | #define PSHM_DEFINED 0x002 | |
122 | #define PSHM_ALLOCATED 0x004 | |
123 | #define PSHM_MAPPED 0x008 | |
124 | #define PSHM_INUSE 0x010 | |
125 | #define PSHM_REMOVED 0x020 | |
126 | #define PSHM_INCREATE 0x040 | |
127 | #define PSHM_INDELETE 0x080 | |
128 | #define PSHM_ALLOCATING 0x100 | |
1c79356b A |
129 | |
130 | struct pshmcache { | |
131 | LIST_ENTRY(pshmcache) pshm_hash; /* hash chain */ | |
132 | struct pshminfo *pshminfo; /* vnode the name refers to */ | |
133 | int pshm_nlen; /* length of name */ | |
134 | char pshm_name[PSHMNAMLEN + 1]; /* segment name */ | |
135 | }; | |
136 | #define PSHMCACHE_NULL (struct pshmcache *)0 | |
137 | ||
138 | struct pshmstats { | |
139 | long goodhits; /* hits that we can really use */ | |
140 | long neghits; /* negative hits that we can use */ | |
141 | long badhits; /* hits we must drop */ | |
142 | long falsehits; /* hits with id mismatch */ | |
143 | long miss; /* misses */ | |
144 | long longnames; /* long names that ignore cache */ | |
145 | }; | |
146 | ||
147 | struct pshmname { | |
148 | char *pshm_nameptr; /* pointer to looked up name */ | |
149 | long pshm_namelen; /* length of looked up component */ | |
150 | u_long pshm_hash; /* hash value of looked up name */ | |
151 | }; | |
152 | ||
153 | struct pshmnode { | |
91447636 | 154 | off_t mapp_addr; |
b0d623f7 | 155 | user_size_t map_size; /* XXX unused ? */ |
1c79356b A |
156 | struct pshminfo *pinfo; |
157 | unsigned int pshm_usecount; | |
158 | #if DIAGNOSTIC | |
159 | unsigned int readcnt; | |
160 | unsigned int writecnt; | |
161 | #endif | |
162 | }; | |
163 | #define PSHMNODE_NULL (struct pshmnode *)0 | |
164 | ||
165 | ||
166 | #define PSHMHASH(pnp) \ | |
167 | (&pshmhashtbl[(pnp)->pshm_hash & pshmhash]) | |
91447636 | 168 | |
1c79356b A |
169 | LIST_HEAD(pshmhashhead, pshmcache) *pshmhashtbl; /* Hash Table */ |
170 | u_long pshmhash; /* size of hash table - 1 */ | |
171 | long pshmnument; /* number of cache entries allocated */ | |
172 | struct pshmstats pshmstats; /* cache effectiveness statistics */ | |
173 | ||
91447636 | 174 | static int pshm_read (struct fileproc *fp, struct uio *uio, |
2d21ac55 | 175 | int flags, vfs_context_t ctx); |
91447636 | 176 | static int pshm_write (struct fileproc *fp, struct uio *uio, |
2d21ac55 | 177 | int flags, vfs_context_t ctx); |
91447636 | 178 | static int pshm_ioctl (struct fileproc *fp, u_long com, |
2d21ac55 A |
179 | caddr_t data, vfs_context_t ctx); |
180 | static int pshm_select (struct fileproc *fp, int which, void *wql, vfs_context_t ctx); | |
6d2010ae | 181 | static int pshm_close(struct pshminfo *pinfo, int dropref); |
2d21ac55 | 182 | static int pshm_closefile (struct fileglob *fg, vfs_context_t ctx); |
91447636 | 183 | |
2d21ac55 | 184 | static int pshm_kqfilter(struct fileproc *fp, struct knote *kn, vfs_context_t ctx); |
91447636 | 185 | |
2d21ac55 | 186 | int pshm_access(struct pshminfo *pinfo, int mode, kauth_cred_t cred, proc_t p); |
91447636 A |
187 | static int pshm_cache_add(struct pshminfo *pshmp, struct pshmname *pnp, struct pshmcache *pcp); |
188 | static void pshm_cache_delete(struct pshmcache *pcp); | |
189 | #if NOT_USED | |
190 | static void pshm_cache_purge(void); | |
191 | #endif /* NOT_USED */ | |
192 | static int pshm_cache_search(struct pshminfo **pshmp, struct pshmname *pnp, | |
6d2010ae | 193 | struct pshmcache **pcache, int addref); |
55e303ae | 194 | |
39236c6e A |
195 | static const struct fileops pshmops = { |
196 | DTYPE_PSXSHM, | |
197 | pshm_read, | |
198 | pshm_write, | |
199 | pshm_ioctl, | |
200 | pshm_select, | |
201 | pshm_closefile, | |
202 | pshm_kqfilter, | |
203 | 0 | |
204 | }; | |
91447636 A |
205 | |
206 | static lck_grp_t *psx_shm_subsys_lck_grp; | |
207 | static lck_grp_attr_t *psx_shm_subsys_lck_grp_attr; | |
208 | static lck_attr_t *psx_shm_subsys_lck_attr; | |
209 | static lck_mtx_t psx_shm_subsys_mutex; | |
210 | ||
211 | #define PSHM_SUBSYS_LOCK() lck_mtx_lock(& psx_shm_subsys_mutex) | |
212 | #define PSHM_SUBSYS_UNLOCK() lck_mtx_unlock(& psx_shm_subsys_mutex) | |
213 | ||
214 | ||
215 | /* Initialize the mutex governing access to the posix shm subsystem */ | |
216 | __private_extern__ void | |
217 | pshm_lock_init( void ) | |
218 | { | |
219 | ||
220 | psx_shm_subsys_lck_grp_attr = lck_grp_attr_alloc_init(); | |
91447636 A |
221 | |
222 | psx_shm_subsys_lck_grp = lck_grp_alloc_init("posix shared memory", psx_shm_subsys_lck_grp_attr); | |
223 | ||
224 | psx_shm_subsys_lck_attr = lck_attr_alloc_init(); | |
91447636 A |
225 | lck_mtx_init(& psx_shm_subsys_mutex, psx_shm_subsys_lck_grp, psx_shm_subsys_lck_attr); |
226 | } | |
1c79356b | 227 | |
1c79356b A |
228 | /* |
229 | * Lookup an entry in the cache | |
230 | * | |
231 | * | |
232 | * status of -1 is returned if matches | |
233 | * If the lookup determines that the name does not exist | |
234 | * (negative cacheing), a status of ENOENT is returned. If the lookup | |
235 | * fails, a status of zero is returned. | |
236 | */ | |
237 | ||
91447636 A |
238 | static int |
239 | pshm_cache_search(struct pshminfo **pshmp, struct pshmname *pnp, | |
6d2010ae | 240 | struct pshmcache **pcache, int addref) |
1c79356b | 241 | { |
91447636 A |
242 | struct pshmcache *pcp, *nnp; |
243 | struct pshmhashhead *pcpp; | |
1c79356b A |
244 | |
245 | if (pnp->pshm_namelen > PSHMNAMLEN) { | |
246 | pshmstats.longnames++; | |
247 | return (0); | |
248 | } | |
249 | ||
250 | pcpp = PSHMHASH(pnp); | |
251 | for (pcp = pcpp->lh_first; pcp != 0; pcp = nnp) { | |
252 | nnp = pcp->pshm_hash.le_next; | |
253 | if (pcp->pshm_nlen == pnp->pshm_namelen && | |
254 | !bcmp(pcp->pshm_name, pnp->pshm_nameptr, (u_int)pcp-> pshm_nlen)) | |
255 | break; | |
256 | } | |
257 | ||
258 | if (pcp == 0) { | |
259 | pshmstats.miss++; | |
260 | return (0); | |
261 | } | |
262 | ||
263 | /* We found a "positive" match, return the vnode */ | |
264 | if (pcp->pshminfo) { | |
265 | pshmstats.goodhits++; | |
266 | /* TOUCH(ncp); */ | |
267 | *pshmp = pcp->pshminfo; | |
268 | *pcache = pcp; | |
6d2010ae A |
269 | if (addref) |
270 | pcp->pshminfo->pshm_usecount++; | |
1c79356b A |
271 | return (-1); |
272 | } | |
273 | ||
274 | /* | |
275 | * We found a "negative" match, ENOENT notifies client of this match. | |
276 | * The nc_vpid field records whether this is a whiteout. | |
277 | */ | |
278 | pshmstats.neghits++; | |
279 | return (ENOENT); | |
280 | } | |
281 | ||
282 | /* | |
283 | * Add an entry to the cache. | |
91447636 | 284 | * XXX should be static? |
1c79356b | 285 | */ |
91447636 A |
286 | static int |
287 | pshm_cache_add(struct pshminfo *pshmp, struct pshmname *pnp, struct pshmcache *pcp) | |
1c79356b | 288 | { |
91447636 | 289 | struct pshmhashhead *pcpp; |
55e303ae A |
290 | struct pshminfo *dpinfo; |
291 | struct pshmcache *dpcp; | |
1c79356b A |
292 | |
293 | #if DIAGNOSTIC | |
2d21ac55 | 294 | if (pnp->pshm_namelen > PSHMNAMLEN) |
1c79356b A |
295 | panic("cache_enter: name too long"); |
296 | #endif | |
297 | ||
91447636 | 298 | |
1c79356b | 299 | /* if the entry has already been added by some one else return */ |
6d2010ae | 300 | if (pshm_cache_search(&dpinfo, pnp, &dpcp, 0) == -1) { |
1c79356b A |
301 | return(EEXIST); |
302 | } | |
303 | pshmnument++; | |
304 | ||
1c79356b A |
305 | /* |
306 | * Fill in cache info, if vp is NULL this is a "negative" cache entry. | |
307 | * For negative entries, we have to record whether it is a whiteout. | |
308 | * the whiteout flag is stored in the nc_vpid field which is | |
309 | * otherwise unused. | |
310 | */ | |
311 | pcp->pshminfo = pshmp; | |
312 | pcp->pshm_nlen = pnp->pshm_namelen; | |
313 | bcopy(pnp->pshm_nameptr, pcp->pshm_name, (unsigned)pcp->pshm_nlen); | |
314 | pcpp = PSHMHASH(pnp); | |
315 | #if DIAGNOSTIC | |
316 | { | |
91447636 | 317 | struct pshmcache *p; |
1c79356b A |
318 | |
319 | for (p = pcpp->lh_first; p != 0; p = p->pshm_hash.le_next) | |
320 | if (p == pcp) | |
321 | panic("cache_enter: duplicate"); | |
322 | } | |
323 | #endif | |
324 | LIST_INSERT_HEAD(pcpp, pcp, pshm_hash); | |
325 | return(0); | |
326 | } | |
327 | ||
328 | /* | |
329 | * Name cache initialization, from vfs_init() when we are booting | |
330 | */ | |
331 | void | |
91447636 | 332 | pshm_cache_init(void) |
1c79356b | 333 | { |
2d21ac55 | 334 | pshmhashtbl = hashinit(desiredvnodes / 8, M_SHM, &pshmhash); |
1c79356b A |
335 | } |
336 | ||
91447636 | 337 | #if NOT_USED |
1c79356b A |
338 | /* |
339 | * Invalidate a all entries to particular vnode. | |
340 | * | |
341 | * We actually just increment the v_id, that will do it. The entries will | |
342 | * be purged by lookup as they get found. If the v_id wraps around, we | |
343 | * need to ditch the entire cache, to avoid confusion. No valid vnode will | |
344 | * ever have (v_id == 0). | |
345 | */ | |
91447636 | 346 | static void |
1c79356b A |
347 | pshm_cache_purge(void) |
348 | { | |
349 | struct pshmcache *pcp; | |
350 | struct pshmhashhead *pcpp; | |
351 | ||
352 | for (pcpp = &pshmhashtbl[pshmhash]; pcpp >= pshmhashtbl; pcpp--) { | |
91447636 | 353 | while ( (pcp = pcpp->lh_first) ) |
1c79356b A |
354 | pshm_cache_delete(pcp); |
355 | } | |
356 | } | |
91447636 | 357 | #endif /* NOT_USED */ |
1c79356b | 358 | |
91447636 A |
359 | static void |
360 | pshm_cache_delete(struct pshmcache *pcp) | |
1c79356b A |
361 | { |
362 | #if DIAGNOSTIC | |
363 | if (pcp->pshm_hash.le_prev == 0) | |
364 | panic("namecache purge le_prev"); | |
365 | if (pcp->pshm_hash.le_next == pcp) | |
366 | panic("namecache purge le_next"); | |
367 | #endif /* DIAGNOSTIC */ | |
368 | LIST_REMOVE(pcp, pshm_hash); | |
369 | pcp->pshm_hash.le_prev = 0; | |
370 | pshmnument--; | |
371 | } | |
372 | ||
373 | ||
1c79356b | 374 | int |
b0d623f7 | 375 | shm_open(proc_t p, struct shm_open_args *uap, int32_t *retval) |
1c79356b | 376 | { |
91447636 | 377 | size_t i; |
91447636 | 378 | int indx, error; |
1c79356b A |
379 | struct pshmname nd; |
380 | struct pshminfo *pinfo; | |
b0d623f7 A |
381 | struct fileproc *fp = NULL; |
382 | char *pnbuf = NULL; | |
383 | struct pshminfo *new_pinfo = PSHMINFO_NULL; | |
384 | struct pshmnode *new_pnode = PSHMNODE_NULL; | |
385 | struct pshmcache *pcache = PSHMCACHE_NULL; /* ignored on return */ | |
1c79356b A |
386 | char * nameptr; |
387 | char * cp; | |
388 | size_t pathlen, plen; | |
389 | int fmode ; | |
390 | int cmode = uap->mode; | |
391 | int incache = 0; | |
b0d623f7 | 392 | struct pshmcache *pcp = NULL; |
1c79356b | 393 | |
e5568f75 A |
394 | AUDIT_ARG(fflags, uap->oflag); |
395 | AUDIT_ARG(mode, uap->mode); | |
91447636 | 396 | |
1c79356b A |
397 | pinfo = PSHMINFO_NULL; |
398 | ||
b0d623f7 A |
399 | /* |
400 | * Preallocate everything we might need up front to avoid taking | |
401 | * and dropping the lock, opening us up to race conditions. | |
402 | */ | |
91447636 A |
403 | MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); |
404 | if (pnbuf == NULL) { | |
b0d623f7 A |
405 | error = ENOSPC; |
406 | goto bad; | |
91447636 A |
407 | } |
408 | ||
1c79356b | 409 | pathlen = MAXPATHLEN; |
91447636 | 410 | error = copyinstr(uap->name, (void *)pnbuf, MAXPATHLEN, &pathlen); |
1c79356b A |
411 | if (error) { |
412 | goto bad; | |
413 | } | |
e5568f75 | 414 | AUDIT_ARG(text, pnbuf); |
1c79356b A |
415 | if (pathlen > PSHMNAMLEN) { |
416 | error = ENAMETOOLONG; | |
417 | goto bad; | |
418 | } | |
1c79356b A |
419 | #ifdef PSXSHM_NAME_RESTRICT |
420 | nameptr = pnbuf; | |
421 | if (*nameptr == '/') { | |
422 | while (*(nameptr++) == '/') { | |
423 | plen--; | |
424 | error = EINVAL; | |
425 | goto bad; | |
426 | } | |
2d21ac55 | 427 | } else { |
1c79356b A |
428 | error = EINVAL; |
429 | goto bad; | |
430 | } | |
431 | #endif /* PSXSHM_NAME_RESTRICT */ | |
432 | ||
433 | plen = pathlen; | |
434 | nameptr = pnbuf; | |
435 | nd.pshm_nameptr = nameptr; | |
436 | nd.pshm_namelen = plen; | |
437 | nd. pshm_hash =0; | |
438 | ||
2d21ac55 A |
439 | for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) { |
440 | nd.pshm_hash += (unsigned char)*cp * i; | |
1c79356b A |
441 | } |
442 | ||
b0d623f7 A |
443 | /* |
444 | * attempt to allocate a new fp; if unsuccessful, the fp will be | |
445 | * left unmodified (NULL). | |
446 | */ | |
447 | error = falloc(p, &fp, &indx, vfs_context_current()); | |
448 | if (error) | |
449 | goto bad; | |
450 | ||
6d2010ae A |
451 | cmode &= ALLPERMS; |
452 | ||
453 | fmode = FFLAGS(uap->oflag); | |
454 | if ((fmode & (FREAD | FWRITE)) == 0) { | |
455 | error = EINVAL; | |
456 | goto bad; | |
457 | } | |
458 | ||
b0d623f7 A |
459 | /* |
460 | * We allocate a new entry if we are less than the maximum | |
461 | * allowed and the one at the front of the LRU list is in use. | |
462 | * Otherwise we use the one at the front of the LRU list. | |
463 | */ | |
464 | MALLOC(pcp, struct pshmcache *, sizeof(struct pshmcache), M_SHM, M_WAITOK|M_ZERO); | |
465 | if (pcp == NULL) { | |
466 | error = ENOSPC; | |
467 | goto bad; | |
468 | } | |
469 | ||
470 | MALLOC(new_pinfo, struct pshminfo *, sizeof(struct pshminfo), M_SHM, M_WAITOK|M_ZERO); | |
471 | if (new_pinfo == PSHMINFO_NULL) { | |
472 | error = ENOSPC; | |
473 | goto bad; | |
474 | } | |
475 | #if CONFIG_MACF | |
476 | mac_posixshm_label_init(new_pinfo); | |
477 | #endif | |
478 | ||
479 | MALLOC(new_pnode, struct pshmnode *, sizeof(struct pshmnode), M_SHM, M_WAITOK|M_ZERO); | |
480 | if (new_pnode == PSHMNODE_NULL) { | |
481 | error = ENOSPC; | |
482 | goto bad; | |
483 | } | |
484 | ||
91447636 | 485 | PSHM_SUBSYS_LOCK(); |
b0d623f7 | 486 | |
6d2010ae A |
487 | /* |
488 | * If we find the entry in the cache, this will take a reference, | |
489 | * allowing us to unlock it for the permissions check. | |
490 | */ | |
491 | error = pshm_cache_search(&pinfo, &nd, &pcache, 1); | |
492 | ||
493 | PSHM_SUBSYS_UNLOCK(); | |
1c79356b A |
494 | |
495 | if (error == ENOENT) { | |
496 | error = EINVAL; | |
6d2010ae | 497 | goto bad; |
1c79356b | 498 | } |
6d2010ae | 499 | |
1c79356b A |
500 | if (!error) { |
501 | incache = 0; | |
6d2010ae A |
502 | if (fmode & O_CREAT) { |
503 | /* create a new one (commit the allocation) */ | |
504 | pinfo = new_pinfo; | |
505 | pinfo->pshm_flags = PSHM_DEFINED | PSHM_INCREATE; | |
506 | pinfo->pshm_usecount = 1; /* existence reference */ | |
507 | pinfo->pshm_mode = cmode; | |
508 | pinfo->pshm_uid = kauth_getuid(); | |
509 | pinfo->pshm_gid = kauth_getgid(); | |
316670eb A |
510 | bcopy(pnbuf, &pinfo->pshm_name[0], pathlen); |
511 | pinfo->pshm_name[pathlen]=0; | |
6d2010ae A |
512 | #if CONFIG_MACF |
513 | error = mac_posixshm_check_create(kauth_cred_get(), nameptr); | |
514 | if (error) { | |
515 | goto bad; | |
516 | } | |
517 | mac_posixshm_label_associate(kauth_cred_get(), pinfo, nameptr); | |
518 | #endif | |
519 | } | |
520 | } else { | |
1c79356b | 521 | incache = 1; |
6d2010ae | 522 | if (fmode & O_CREAT) { |
b0d623f7 A |
523 | /* already exists */ |
524 | if ((fmode & O_EXCL)) { | |
525 | AUDIT_ARG(posix_ipc_perm, pinfo->pshm_uid, | |
526 | pinfo->pshm_gid, | |
527 | pinfo->pshm_mode); | |
528 | ||
529 | /* shm obj exists and opened O_EXCL */ | |
530 | error = EEXIST; | |
6d2010ae | 531 | goto bad; |
b0d623f7 A |
532 | } |
533 | ||
534 | if( pinfo->pshm_flags & PSHM_INDELETE) { | |
535 | error = ENOENT; | |
6d2010ae | 536 | goto bad; |
b0d623f7 | 537 | } |
e5568f75 | 538 | AUDIT_ARG(posix_ipc_perm, pinfo->pshm_uid, |
2d21ac55 | 539 | pinfo->pshm_gid, pinfo->pshm_mode); |
b0d623f7 | 540 | #if CONFIG_MACF |
316670eb | 541 | if ((error = mac_posixshm_check_open(kauth_cred_get(), pinfo, fmode))) { |
6d2010ae | 542 | goto bad; |
2d21ac55 | 543 | } |
b0d623f7 A |
544 | #endif |
545 | if ( (error = pshm_access(pinfo, fmode, kauth_cred_get(), p)) ) { | |
6d2010ae | 546 | goto bad; |
2d21ac55 | 547 | } |
2d21ac55 | 548 | } |
6d2010ae A |
549 | } |
550 | if (!(fmode & O_CREAT)) { | |
1c79356b | 551 | if (!incache) { |
b0d623f7 | 552 | /* O_CREAT is not set and the object does not exist */ |
1c79356b | 553 | error = ENOENT; |
6d2010ae | 554 | goto bad; |
1c79356b A |
555 | } |
556 | if( pinfo->pshm_flags & PSHM_INDELETE) { | |
557 | error = ENOENT; | |
6d2010ae | 558 | goto bad; |
1c79356b | 559 | } |
2d21ac55 | 560 | #if CONFIG_MACF |
316670eb | 561 | if ((error = mac_posixshm_check_open(kauth_cred_get(), pinfo, fmode))) { |
6d2010ae | 562 | goto bad; |
2d21ac55 A |
563 | } |
564 | #endif | |
565 | ||
b0d623f7 | 566 | if ((error = pshm_access(pinfo, fmode, kauth_cred_get(), p))) { |
6d2010ae | 567 | goto bad; |
91447636 | 568 | } |
1c79356b A |
569 | } |
570 | if (fmode & O_TRUNC) { | |
571 | error = EINVAL; | |
6d2010ae | 572 | goto bad; |
1c79356b | 573 | } |
6d2010ae A |
574 | |
575 | ||
576 | PSHM_SUBSYS_LOCK(); | |
577 | ||
1c79356b A |
578 | #if DIAGNOSTIC |
579 | if (fmode & FWRITE) | |
580 | pinfo->pshm_writecount++; | |
581 | if (fmode & FREAD) | |
582 | pinfo->pshm_readcount++; | |
583 | #endif | |
1c79356b | 584 | if (!incache) { |
b0d623f7 | 585 | /* if successful, this will consume the pcp */ |
91447636 | 586 | if ( (error = pshm_cache_add(pinfo, &nd, pcp)) ) { |
b0d623f7 | 587 | goto bad_locked; |
1c79356b | 588 | } |
6d2010ae A |
589 | /* |
590 | * add reference for the new entry; otherwise, we obtained | |
591 | * one from the cache hit earlier. | |
592 | */ | |
593 | pinfo->pshm_usecount++; | |
1c79356b A |
594 | } |
595 | pinfo->pshm_flags &= ~PSHM_INCREATE; | |
b0d623f7 | 596 | new_pnode->pinfo = pinfo; |
91447636 A |
597 | |
598 | PSHM_SUBSYS_UNLOCK(); | |
b0d623f7 A |
599 | |
600 | /* | |
601 | * if incache, we did not use the new pcp or new_pinfo and must | |
602 | * free them | |
603 | */ | |
604 | if (incache) { | |
605 | FREE(pcp, M_SHM); | |
606 | ||
607 | if (new_pinfo != PSHMINFO_NULL) { | |
608 | #if CONFIG_MACF | |
609 | mac_posixshm_label_destroy(new_pinfo); | |
610 | #endif | |
611 | FREE(new_pinfo, M_SHM); | |
612 | } | |
613 | } | |
614 | ||
91447636 | 615 | proc_fdlock(p); |
1c79356b | 616 | fp->f_flag = fmode & FMASK; |
1c79356b | 617 | fp->f_ops = &pshmops; |
b0d623f7 | 618 | fp->f_data = (caddr_t)new_pnode; |
2d21ac55 | 619 | *fdflags(p, indx) |= UF_EXCLOSE; |
6601e61a | 620 | procfdtbl_releasefd(p, indx, NULL); |
91447636 A |
621 | fp_drop(p, indx, fp, 1); |
622 | proc_fdunlock(p); | |
623 | ||
1c79356b | 624 | *retval = indx; |
55e303ae | 625 | FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI); |
1c79356b | 626 | return (0); |
2d21ac55 | 627 | |
b0d623f7 A |
628 | bad_locked: |
629 | PSHM_SUBSYS_UNLOCK(); | |
630 | bad: | |
6d2010ae A |
631 | /* |
632 | * If we obtained the entry from the cache, we need to drop the | |
633 | * reference; holding the reference may have prevented unlinking, | |
634 | * so we need to call pshm_close() to get the full effect. | |
635 | */ | |
636 | if (incache) { | |
637 | PSHM_SUBSYS_LOCK(); | |
638 | pshm_close(pinfo, 1); | |
639 | PSHM_SUBSYS_UNLOCK(); | |
640 | } | |
641 | ||
b0d623f7 A |
642 | if (pcp != NULL) |
643 | FREE(pcp, M_SHM); | |
644 | ||
645 | if (new_pnode != PSHMNODE_NULL) | |
646 | FREE(new_pnode, M_SHM); | |
647 | ||
648 | if (fp != NULL) | |
649 | fp_free(p, indx, fp); | |
650 | ||
651 | if (new_pinfo != PSHMINFO_NULL) { | |
2d21ac55 | 652 | #if CONFIG_MACF |
b0d623f7 | 653 | mac_posixshm_label_destroy(new_pinfo); |
2d21ac55 | 654 | #endif |
b0d623f7 | 655 | FREE(new_pinfo, M_SHM); |
2d21ac55 | 656 | } |
b0d623f7 A |
657 | if (pnbuf != NULL) |
658 | FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI); | |
1c79356b A |
659 | return (error); |
660 | } | |
661 | ||
662 | ||
1c79356b | 663 | int |
2d21ac55 | 664 | pshm_truncate(__unused proc_t p, struct fileproc *fp, __unused int fd, |
b0d623f7 | 665 | off_t length, __unused int32_t *retval) |
1c79356b A |
666 | { |
667 | struct pshminfo * pinfo; | |
668 | struct pshmnode * pnode ; | |
669 | kern_return_t kret; | |
91447636 | 670 | mem_entry_name_port_t mem_object; |
6d2010ae A |
671 | mach_vm_size_t total_size, alloc_size; |
672 | memory_object_size_t mosize; | |
b0d623f7 | 673 | struct pshmobj *pshmobj, *pshmobj_next, **pshmobj_next_p; |
39236c6e | 674 | vm_map_t user_map; |
2d21ac55 A |
675 | #if CONFIG_MACF |
676 | int error; | |
677 | #endif | |
1c79356b | 678 | |
39236c6e A |
679 | user_map = current_map(); |
680 | ||
1c79356b A |
681 | if (fp->f_type != DTYPE_PSXSHM) { |
682 | return(EINVAL); | |
683 | } | |
684 | ||
685 | ||
686 | if (((pnode = (struct pshmnode *)fp->f_data)) == PSHMNODE_NULL ) | |
687 | return(EINVAL); | |
688 | ||
91447636 A |
689 | PSHM_SUBSYS_LOCK(); |
690 | if ((pinfo = pnode->pinfo) == PSHMINFO_NULL) { | |
691 | PSHM_SUBSYS_UNLOCK(); | |
1c79356b | 692 | return(EINVAL); |
91447636 | 693 | } |
b0d623f7 | 694 | if ((pinfo->pshm_flags & (PSHM_DEFINED|PSHM_ALLOCATING|PSHM_ALLOCATED)) |
1c79356b | 695 | != PSHM_DEFINED) { |
91447636 | 696 | PSHM_SUBSYS_UNLOCK(); |
1c79356b A |
697 | return(EINVAL); |
698 | } | |
2d21ac55 | 699 | #if CONFIG_MACF |
6d2010ae | 700 | error = mac_posixshm_check_truncate(kauth_cred_get(), pinfo, length); |
2d21ac55 A |
701 | if (error) { |
702 | PSHM_SUBSYS_UNLOCK(); | |
703 | return(error); | |
704 | } | |
705 | #endif | |
1c79356b | 706 | |
b0d623f7 | 707 | pinfo->pshm_flags |= PSHM_ALLOCATING; |
39236c6e A |
708 | total_size = vm_map_round_page(length, |
709 | vm_map_page_mask(user_map)); | |
b0d623f7 | 710 | pshmobj_next_p = &pinfo->pshm_memobjects; |
1c79356b | 711 | |
b0d623f7 A |
712 | for (alloc_size = 0; |
713 | alloc_size < total_size; | |
6d2010ae | 714 | alloc_size += mosize) { |
1c79356b | 715 | |
b0d623f7 A |
716 | PSHM_SUBSYS_UNLOCK(); |
717 | ||
6d2010ae | 718 | mosize = MIN(total_size - alloc_size, ANON_MAX_SIZE); |
b0d623f7 A |
719 | kret = mach_make_memory_entry_64( |
720 | VM_MAP_NULL, | |
6d2010ae | 721 | &mosize, |
b0d623f7 A |
722 | 0, |
723 | MAP_MEM_NAMED_CREATE | VM_PROT_DEFAULT, | |
724 | &mem_object, | |
725 | 0); | |
726 | ||
727 | if (kret != KERN_SUCCESS) | |
728 | goto out; | |
729 | ||
730 | MALLOC(pshmobj, struct pshmobj *, sizeof (struct pshmobj), | |
731 | M_SHM, M_WAITOK); | |
732 | if (pshmobj == NULL) { | |
733 | kret = KERN_NO_SPACE; | |
734 | mach_memory_entry_port_release(mem_object); | |
735 | mem_object = NULL; | |
736 | goto out; | |
737 | } | |
738 | ||
739 | PSHM_SUBSYS_LOCK(); | |
740 | ||
741 | pshmobj->pshmo_memobject = (void *) mem_object; | |
6d2010ae | 742 | pshmobj->pshmo_size = mosize; |
b0d623f7 A |
743 | pshmobj->pshmo_next = NULL; |
744 | ||
745 | *pshmobj_next_p = pshmobj; | |
746 | pshmobj_next_p = &pshmobj->pshmo_next; | |
747 | } | |
748 | ||
1c79356b | 749 | pinfo->pshm_flags = PSHM_ALLOCATED; |
b0d623f7 | 750 | pinfo->pshm_length = total_size; |
91447636 | 751 | PSHM_SUBSYS_UNLOCK(); |
1c79356b A |
752 | return(0); |
753 | ||
754 | out: | |
b0d623f7 A |
755 | PSHM_SUBSYS_LOCK(); |
756 | for (pshmobj = pinfo->pshm_memobjects; | |
757 | pshmobj != NULL; | |
758 | pshmobj = pshmobj_next) { | |
759 | pshmobj_next = pshmobj->pshmo_next; | |
760 | mach_memory_entry_port_release(pshmobj->pshmo_memobject); | |
761 | FREE(pshmobj, M_SHM); | |
762 | } | |
763 | pinfo->pshm_memobjects = NULL; | |
764 | pinfo->pshm_flags &= ~PSHM_ALLOCATING; | |
765 | PSHM_SUBSYS_UNLOCK(); | |
766 | ||
1c79356b A |
767 | switch (kret) { |
768 | case KERN_INVALID_ADDRESS: | |
769 | case KERN_NO_SPACE: | |
770 | return (ENOMEM); | |
771 | case KERN_PROTECTION_FAILURE: | |
772 | return (EACCES); | |
773 | default: | |
774 | return (EINVAL); | |
775 | ||
776 | } | |
777 | } | |
778 | ||
779 | int | |
2d21ac55 | 780 | pshm_stat(struct pshmnode *pnode, void *ub, int isstat64) |
1c79356b | 781 | { |
2d21ac55 A |
782 | struct stat *sb = (struct stat *)0; /* warning avoidance ; protected by isstat64 */ |
783 | struct stat64 * sb64 = (struct stat64 *)0; /* warning avoidance ; protected by isstat64 */ | |
1c79356b | 784 | struct pshminfo *pinfo; |
2d21ac55 A |
785 | #if CONFIG_MACF |
786 | int error; | |
787 | #endif | |
1c79356b | 788 | |
91447636 A |
789 | PSHM_SUBSYS_LOCK(); |
790 | if ((pinfo = pnode->pinfo) == PSHMINFO_NULL){ | |
791 | PSHM_SUBSYS_UNLOCK(); | |
1c79356b | 792 | return(EINVAL); |
91447636 | 793 | } |
1c79356b | 794 | |
2d21ac55 A |
795 | #if CONFIG_MACF |
796 | error = mac_posixshm_check_stat(kauth_cred_get(), pinfo); | |
797 | if (error) { | |
798 | PSHM_SUBSYS_UNLOCK(); | |
799 | return(error); | |
800 | } | |
801 | #endif | |
802 | ||
803 | if (isstat64 != 0) { | |
804 | sb64 = (struct stat64 *)ub; | |
805 | bzero(sb64, sizeof(struct stat64)); | |
806 | sb64->st_mode = pinfo->pshm_mode; | |
807 | sb64->st_uid = pinfo->pshm_uid; | |
808 | sb64->st_gid = pinfo->pshm_gid; | |
809 | sb64->st_size = pinfo->pshm_length; | |
810 | } else { | |
811 | sb = (struct stat *)ub; | |
812 | bzero(sb, sizeof(struct stat)); | |
813 | sb->st_mode = pinfo->pshm_mode; | |
814 | sb->st_uid = pinfo->pshm_uid; | |
815 | sb->st_gid = pinfo->pshm_gid; | |
816 | sb->st_size = pinfo->pshm_length; | |
817 | } | |
91447636 | 818 | PSHM_SUBSYS_UNLOCK(); |
1c79356b A |
819 | |
820 | return(0); | |
821 | } | |
822 | ||
91447636 A |
823 | /* |
824 | * This is called only from shm_open which holds pshm_lock(); | |
825 | * XXX This code is repeated many times | |
826 | */ | |
1c79356b | 827 | int |
2d21ac55 | 828 | pshm_access(struct pshminfo *pinfo, int mode, kauth_cred_t cred, __unused proc_t p) |
1c79356b | 829 | { |
6d2010ae A |
830 | int mode_req = ((mode & FREAD) ? S_IRUSR : 0) | |
831 | ((mode & FWRITE) ? S_IWUSR : 0); | |
1c79356b A |
832 | |
833 | /* Otherwise, user id 0 always gets access. */ | |
91447636 | 834 | if (!suser(cred, NULL)) |
1c79356b A |
835 | return (0); |
836 | ||
6d2010ae | 837 | return(posix_cred_access(cred, pinfo->pshm_uid, pinfo->pshm_gid, pinfo->pshm_mode, mode_req)); |
1c79356b | 838 | } |
9bccf70c | 839 | |
1c79356b | 840 | int |
2d21ac55 | 841 | pshm_mmap(__unused proc_t p, struct mmap_args *uap, user_addr_t *retval, struct fileproc *fp, off_t pageoff) |
1c79356b | 842 | { |
316670eb A |
843 | vm_map_offset_t user_addr = (vm_map_offset_t)uap->addr; |
844 | vm_map_size_t user_size = (vm_map_size_t)uap->len ; | |
845 | vm_map_offset_t user_start_addr; | |
846 | vm_map_size_t map_size, mapped_size; | |
1c79356b A |
847 | int prot = uap->prot; |
848 | int flags = uap->flags; | |
849 | vm_object_offset_t file_pos = (vm_object_offset_t)uap->pos; | |
b0d623f7 | 850 | vm_object_offset_t map_pos; |
1c79356b | 851 | vm_map_t user_map; |
91447636 A |
852 | int alloc_flags; |
853 | boolean_t docow; | |
1c79356b A |
854 | kern_return_t kret; |
855 | struct pshminfo * pinfo; | |
856 | struct pshmnode * pnode; | |
b0d623f7 | 857 | struct pshmobj * pshmobj; |
2d21ac55 A |
858 | #if CONFIG_MACF |
859 | int error; | |
860 | #endif | |
1c79356b A |
861 | |
862 | if (user_size == 0) | |
863 | return(0); | |
864 | ||
865 | if ((flags & MAP_SHARED) == 0) | |
866 | return(EINVAL); | |
867 | ||
868 | ||
869 | if ((prot & PROT_WRITE) && ((fp->f_flag & FWRITE) == 0)) { | |
870 | return(EPERM); | |
871 | } | |
872 | ||
873 | if (((pnode = (struct pshmnode *)fp->f_data)) == PSHMNODE_NULL ) | |
874 | return(EINVAL); | |
875 | ||
91447636 A |
876 | PSHM_SUBSYS_LOCK(); |
877 | if ((pinfo = pnode->pinfo) == PSHMINFO_NULL) { | |
878 | PSHM_SUBSYS_UNLOCK(); | |
1c79356b | 879 | return(EINVAL); |
91447636 | 880 | } |
1c79356b A |
881 | |
882 | if ((pinfo->pshm_flags & PSHM_ALLOCATED) != PSHM_ALLOCATED) { | |
91447636 | 883 | PSHM_SUBSYS_UNLOCK(); |
1c79356b A |
884 | return(EINVAL); |
885 | } | |
91447636 A |
886 | if ((off_t)user_size > pinfo->pshm_length) { |
887 | PSHM_SUBSYS_UNLOCK(); | |
1c79356b A |
888 | return(EINVAL); |
889 | } | |
91447636 A |
890 | if ((off_t)(user_size + file_pos) > pinfo->pshm_length) { |
891 | PSHM_SUBSYS_UNLOCK(); | |
1c79356b A |
892 | return(EINVAL); |
893 | } | |
b0d623f7 | 894 | if ((pshmobj = pinfo->pshm_memobjects) == NULL) { |
91447636 | 895 | PSHM_SUBSYS_UNLOCK(); |
1c79356b A |
896 | return(EINVAL); |
897 | } | |
898 | ||
2d21ac55 A |
899 | #if CONFIG_MACF |
900 | error = mac_posixshm_check_mmap(kauth_cred_get(), pinfo, prot, flags); | |
901 | if (error) { | |
902 | PSHM_SUBSYS_UNLOCK(); | |
903 | return(error); | |
904 | } | |
905 | #endif | |
91447636 A |
906 | |
907 | PSHM_SUBSYS_UNLOCK(); | |
1c79356b A |
908 | user_map = current_map(); |
909 | ||
910 | if ((flags & MAP_FIXED) == 0) { | |
91447636 | 911 | alloc_flags = VM_FLAGS_ANYWHERE; |
39236c6e A |
912 | user_addr = vm_map_round_page(user_addr, |
913 | vm_map_page_mask(user_map)); | |
1c79356b | 914 | } else { |
39236c6e A |
915 | if (user_addr != vm_map_round_page(user_addr, |
916 | vm_map_page_mask(user_map))) | |
1c79356b | 917 | return (EINVAL); |
91447636 A |
918 | /* |
919 | * We do not get rid of the existing mappings here because | |
920 | * it wouldn't be atomic (see comment in mmap()). We let | |
921 | * Mach VM know that we want it to replace any existing | |
922 | * mapping with the new one. | |
923 | */ | |
924 | alloc_flags = VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE; | |
1c79356b A |
925 | } |
926 | docow = FALSE; | |
927 | ||
b0d623f7 A |
928 | mapped_size = 0; |
929 | ||
930 | /* reserver the entire space first... */ | |
931 | kret = vm_map_enter_mem_object(user_map, | |
932 | &user_addr, | |
933 | user_size, | |
934 | 0, | |
935 | alloc_flags, | |
936 | IPC_PORT_NULL, | |
937 | 0, | |
938 | FALSE, | |
939 | VM_PROT_NONE, | |
940 | VM_PROT_NONE, | |
941 | VM_INHERIT_NONE); | |
942 | user_start_addr = user_addr; | |
1c79356b | 943 | if (kret != KERN_SUCCESS) { |
1c79356b A |
944 | goto out; |
945 | } | |
b0d623f7 A |
946 | |
947 | /* ... and overwrite with the real mappings */ | |
948 | for (map_pos = 0, pshmobj = pinfo->pshm_memobjects; | |
949 | user_size != 0; | |
950 | map_pos += pshmobj->pshmo_size, pshmobj = pshmobj->pshmo_next) { | |
951 | if (pshmobj == NULL) { | |
952 | /* nothing there to map !? */ | |
953 | goto out; | |
954 | } | |
955 | if (file_pos >= map_pos + pshmobj->pshmo_size) { | |
956 | continue; | |
957 | } | |
958 | map_size = pshmobj->pshmo_size - (file_pos - map_pos); | |
959 | if (map_size > user_size) { | |
960 | map_size = user_size; | |
961 | } | |
962 | kret = vm_map_enter_mem_object( | |
963 | user_map, | |
964 | &user_addr, | |
965 | map_size, | |
966 | 0, | |
967 | VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, | |
968 | pshmobj->pshmo_memobject, | |
969 | file_pos - map_pos, | |
970 | docow, | |
971 | prot, | |
972 | VM_PROT_DEFAULT, | |
973 | VM_INHERIT_SHARE); | |
974 | if (kret != KERN_SUCCESS) | |
975 | goto out; | |
976 | ||
977 | user_addr += map_size; | |
978 | user_size -= map_size; | |
979 | mapped_size += map_size; | |
980 | file_pos += map_size; | |
981 | } | |
982 | ||
91447636 | 983 | PSHM_SUBSYS_LOCK(); |
b0d623f7 A |
984 | pnode->mapp_addr = user_start_addr; |
985 | pnode->map_size = mapped_size; | |
1c79356b | 986 | pinfo->pshm_flags |= (PSHM_MAPPED | PSHM_INUSE); |
91447636 | 987 | PSHM_SUBSYS_UNLOCK(); |
1c79356b | 988 | out: |
b0d623f7 A |
989 | if (kret != KERN_SUCCESS) { |
990 | if (mapped_size != 0) { | |
991 | (void) mach_vm_deallocate(current_map(), | |
992 | user_start_addr, | |
993 | mapped_size); | |
994 | } | |
995 | } | |
996 | ||
1c79356b A |
997 | switch (kret) { |
998 | case KERN_SUCCESS: | |
b0d623f7 | 999 | *retval = (user_start_addr + pageoff); |
1c79356b A |
1000 | return (0); |
1001 | case KERN_INVALID_ADDRESS: | |
1002 | case KERN_NO_SPACE: | |
1003 | return (ENOMEM); | |
1004 | case KERN_PROTECTION_FAILURE: | |
1005 | return (EACCES); | |
1006 | default: | |
1007 | return (EINVAL); | |
1008 | } | |
1009 | ||
1010 | } | |
1011 | ||
1c79356b | 1012 | int |
2d21ac55 | 1013 | shm_unlink(__unused proc_t p, struct shm_unlink_args *uap, |
b0d623f7 | 1014 | __unused int32_t *retval) |
1c79356b | 1015 | { |
91447636 | 1016 | size_t i; |
1c79356b A |
1017 | int error=0; |
1018 | struct pshmname nd; | |
1019 | struct pshminfo *pinfo; | |
1c79356b A |
1020 | char * pnbuf; |
1021 | char * nameptr; | |
1022 | char * cp; | |
1023 | size_t pathlen, plen; | |
1c79356b | 1024 | int incache = 0; |
1c79356b | 1025 | struct pshmcache *pcache = PSHMCACHE_NULL; |
b0d623f7 | 1026 | struct pshmobj *pshmobj, *pshmobj_next; |
1c79356b A |
1027 | |
1028 | pinfo = PSHMINFO_NULL; | |
1029 | ||
91447636 A |
1030 | MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); |
1031 | if (pnbuf == NULL) { | |
1032 | return(ENOSPC); /* XXX non-standard */ | |
1033 | } | |
1c79356b | 1034 | pathlen = MAXPATHLEN; |
91447636 | 1035 | error = copyinstr(uap->name, (void *)pnbuf, MAXPATHLEN, &pathlen); |
1c79356b A |
1036 | if (error) { |
1037 | goto bad; | |
1038 | } | |
e5568f75 | 1039 | AUDIT_ARG(text, pnbuf); |
1c79356b A |
1040 | if (pathlen > PSHMNAMLEN) { |
1041 | error = ENAMETOOLONG; | |
1042 | goto bad; | |
1043 | } | |
1044 | ||
1045 | ||
1046 | #ifdef PSXSHM_NAME_RESTRICT | |
1047 | nameptr = pnbuf; | |
1048 | if (*nameptr == '/') { | |
1049 | while (*(nameptr++) == '/') { | |
1050 | plen--; | |
1051 | error = EINVAL; | |
1052 | goto bad; | |
1053 | } | |
1054 | } else { | |
1055 | error = EINVAL; | |
1056 | goto bad; | |
1057 | } | |
1058 | #endif /* PSXSHM_NAME_RESTRICT */ | |
1059 | ||
1060 | plen = pathlen; | |
1061 | nameptr = pnbuf; | |
1062 | nd.pshm_nameptr = nameptr; | |
1063 | nd.pshm_namelen = plen; | |
1064 | nd. pshm_hash =0; | |
1065 | ||
1066 | for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) { | |
1067 | nd.pshm_hash += (unsigned char)*cp * i; | |
1068 | } | |
1069 | ||
91447636 | 1070 | PSHM_SUBSYS_LOCK(); |
6d2010ae | 1071 | error = pshm_cache_search(&pinfo, &nd, &pcache, 0); |
1c79356b A |
1072 | |
1073 | if (error == ENOENT) { | |
91447636 | 1074 | PSHM_SUBSYS_UNLOCK(); |
1c79356b A |
1075 | goto bad; |
1076 | ||
1077 | } | |
39236c6e | 1078 | /* During unlink lookup failure also implies ENOENT */ |
1c79356b | 1079 | if (!error) { |
91447636 | 1080 | PSHM_SUBSYS_UNLOCK(); |
39236c6e | 1081 | error = ENOENT; |
1c79356b A |
1082 | goto bad; |
1083 | } else | |
1084 | incache = 1; | |
1085 | ||
1086 | if ((pinfo->pshm_flags & (PSHM_DEFINED | PSHM_ALLOCATED))==0) { | |
91447636 | 1087 | PSHM_SUBSYS_UNLOCK(); |
2d21ac55 A |
1088 | error = EINVAL; |
1089 | goto bad; | |
1c79356b A |
1090 | } |
1091 | ||
b0d623f7 A |
1092 | if (pinfo->pshm_flags & PSHM_ALLOCATING) { |
1093 | /* XXX should we wait for flag to clear and then proceed ? */ | |
1094 | PSHM_SUBSYS_UNLOCK(); | |
1095 | error = EAGAIN; | |
1096 | goto bad; | |
1097 | } | |
1098 | ||
1c79356b | 1099 | if (pinfo->pshm_flags & PSHM_INDELETE) { |
91447636 | 1100 | PSHM_SUBSYS_UNLOCK(); |
1c79356b A |
1101 | error = 0; |
1102 | goto bad; | |
1103 | } | |
2d21ac55 A |
1104 | #if CONFIG_MACF |
1105 | error = mac_posixshm_check_unlink(kauth_cred_get(), pinfo, nameptr); | |
1106 | if (error) { | |
1107 | PSHM_SUBSYS_UNLOCK(); | |
1108 | goto bad; | |
1109 | } | |
1110 | #endif | |
1c79356b | 1111 | |
e5568f75 A |
1112 | AUDIT_ARG(posix_ipc_perm, pinfo->pshm_uid, pinfo->pshm_gid, |
1113 | pinfo->pshm_mode); | |
91447636 | 1114 | |
316670eb A |
1115 | /* |
1116 | * following file semantics, unlink should be allowed | |
1117 | * for users with write permission only. | |
91447636 | 1118 | */ |
316670eb A |
1119 | if ( (error = pshm_access(pinfo, FWRITE, kauth_cred_get(), p)) ) { |
1120 | PSHM_SUBSYS_UNLOCK(); | |
1121 | goto bad; | |
1122 | } | |
91447636 | 1123 | |
1c79356b | 1124 | pinfo->pshm_flags |= PSHM_INDELETE; |
1c79356b | 1125 | pshm_cache_delete(pcache); |
1c79356b | 1126 | pinfo->pshm_flags |= PSHM_REMOVED; |
91447636 A |
1127 | /* release the existence reference */ |
1128 | if (!--pinfo->pshm_usecount) { | |
316670eb A |
1129 | #if CONFIG_MACF |
1130 | mac_posixshm_label_destroy(pinfo); | |
1131 | #endif | |
91447636 A |
1132 | PSHM_SUBSYS_UNLOCK(); |
1133 | /* | |
1134 | * If this is the last reference going away on the object, | |
1135 | * then we need to destroy the backing object. The name | |
1136 | * has an implied but uncounted reference on the object, | |
b0d623f7 | 1137 | * once it's created, since it's used as a rendezvous, and |
91447636 A |
1138 | * therefore may be subsequently reopened. |
1139 | */ | |
b0d623f7 A |
1140 | for (pshmobj = pinfo->pshm_memobjects; |
1141 | pshmobj != NULL; | |
1142 | pshmobj = pshmobj_next) { | |
1143 | mach_memory_entry_port_release(pshmobj->pshmo_memobject); | |
1144 | pshmobj_next = pshmobj->pshmo_next; | |
1145 | FREE(pshmobj, M_SHM); | |
1146 | } | |
91447636 | 1147 | FREE(pinfo,M_SHM); |
b0d623f7 A |
1148 | } else { |
1149 | PSHM_SUBSYS_UNLOCK(); | |
91447636 | 1150 | } |
91447636 | 1151 | FREE(pcache, M_SHM); |
1c79356b A |
1152 | error = 0; |
1153 | bad: | |
55e303ae | 1154 | FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI); |
1c79356b | 1155 | return (error); |
1c79356b | 1156 | } |
1c79356b | 1157 | |
91447636 A |
1158 | /* already called locked */ |
1159 | static int | |
6d2010ae | 1160 | pshm_close(struct pshminfo *pinfo, int dropref) |
1c79356b | 1161 | { |
6d2010ae | 1162 | int error = 0; |
b0d623f7 | 1163 | struct pshmobj *pshmobj, *pshmobj_next; |
1c79356b | 1164 | |
6d2010ae A |
1165 | /* |
1166 | * If we are dropping the reference we took on the cache object, don't | |
1167 | * enforce the allocation requirement. | |
1168 | */ | |
1169 | if ( !dropref && ((pinfo->pshm_flags & PSHM_ALLOCATED) != PSHM_ALLOCATED)) { | |
1c79356b A |
1170 | return(EINVAL); |
1171 | } | |
1172 | #if DIAGNOSTIC | |
1173 | if(!pinfo->pshm_usecount) { | |
1174 | kprintf("negative usecount in pshm_close\n"); | |
1175 | } | |
1176 | #endif /* DIAGNOSTIC */ | |
91447636 | 1177 | pinfo->pshm_usecount--; /* release this fd's reference */ |
1c79356b A |
1178 | |
1179 | if ((pinfo->pshm_flags & PSHM_REMOVED) && !pinfo->pshm_usecount) { | |
b0d623f7 A |
1180 | #if CONFIG_MACF |
1181 | mac_posixshm_label_destroy(pinfo); | |
1182 | #endif | |
91447636 A |
1183 | PSHM_SUBSYS_UNLOCK(); |
1184 | /* | |
1185 | * If this is the last reference going away on the object, | |
1186 | * then we need to destroy the backing object. | |
1187 | */ | |
b0d623f7 A |
1188 | for (pshmobj = pinfo->pshm_memobjects; |
1189 | pshmobj != NULL; | |
1190 | pshmobj = pshmobj_next) { | |
1191 | mach_memory_entry_port_release(pshmobj->pshmo_memobject); | |
1192 | pshmobj_next = pshmobj->pshmo_next; | |
1193 | FREE(pshmobj, M_SHM); | |
1194 | } | |
91447636 A |
1195 | PSHM_SUBSYS_LOCK(); |
1196 | FREE(pinfo,M_SHM); | |
1197 | } | |
1c79356b A |
1198 | return (error); |
1199 | } | |
9bccf70c | 1200 | |
2d21ac55 | 1201 | /* vfs_context_t passed to match prototype for struct fileops */ |
9bccf70c | 1202 | static int |
2d21ac55 | 1203 | pshm_closefile(struct fileglob *fg, __unused vfs_context_t ctx) |
9bccf70c | 1204 | { |
6d2010ae A |
1205 | int error = EINVAL; |
1206 | struct pshmnode *pnode; | |
91447636 A |
1207 | |
1208 | PSHM_SUBSYS_LOCK(); | |
6d2010ae A |
1209 | |
1210 | if ((pnode = (struct pshmnode *)fg->fg_data) != NULL) { | |
1211 | if (pnode->pinfo != PSHMINFO_NULL) { | |
1212 | error = pshm_close(pnode->pinfo, 0); | |
1213 | } | |
1214 | FREE(pnode, M_SHM); | |
1215 | } | |
1216 | ||
91447636 | 1217 | PSHM_SUBSYS_UNLOCK(); |
6d2010ae | 1218 | |
91447636 | 1219 | return(error); |
9bccf70c A |
1220 | } |
1221 | ||
1222 | static int | |
91447636 | 1223 | pshm_read(__unused struct fileproc *fp, __unused struct uio *uio, |
2d21ac55 | 1224 | __unused int flags, __unused vfs_context_t ctx) |
1c79356b | 1225 | { |
91447636 | 1226 | return(ENOTSUP); |
1c79356b | 1227 | } |
9bccf70c A |
1228 | |
1229 | static int | |
91447636 | 1230 | pshm_write(__unused struct fileproc *fp, __unused struct uio *uio, |
2d21ac55 | 1231 | __unused int flags, __unused vfs_context_t ctx) |
1c79356b | 1232 | { |
91447636 | 1233 | return(ENOTSUP); |
1c79356b | 1234 | } |
9bccf70c A |
1235 | |
1236 | static int | |
91447636 | 1237 | pshm_ioctl(__unused struct fileproc *fp, __unused u_long com, |
2d21ac55 | 1238 | __unused caddr_t data, __unused vfs_context_t ctx) |
1c79356b | 1239 | { |
91447636 | 1240 | return(ENOTSUP); |
1c79356b | 1241 | } |
9bccf70c A |
1242 | |
1243 | static int | |
91447636 | 1244 | pshm_select(__unused struct fileproc *fp, __unused int which, __unused void *wql, |
2d21ac55 | 1245 | __unused vfs_context_t ctx) |
1c79356b | 1246 | { |
91447636 | 1247 | return(ENOTSUP); |
1c79356b | 1248 | } |
55e303ae A |
1249 | |
1250 | static int | |
91447636 | 1251 | pshm_kqfilter(__unused struct fileproc *fp, __unused struct knote *kn, |
2d21ac55 | 1252 | __unused vfs_context_t ctx) |
55e303ae | 1253 | { |
91447636 | 1254 | return(ENOTSUP); |
55e303ae | 1255 | } |
0c530ab8 A |
1256 | |
1257 | int | |
1258 | fill_pshminfo(struct pshmnode * pshm, struct pshm_info * info) | |
1259 | { | |
1260 | struct pshminfo *pinfo; | |
2d21ac55 | 1261 | struct vinfo_stat *sb; |
0c530ab8 A |
1262 | |
1263 | PSHM_SUBSYS_LOCK(); | |
1264 | if ((pinfo = pshm->pinfo) == PSHMINFO_NULL){ | |
1265 | PSHM_SUBSYS_UNLOCK(); | |
1266 | return(EINVAL); | |
1267 | } | |
1268 | ||
1269 | sb = &info->pshm_stat; | |
1270 | ||
2d21ac55 A |
1271 | bzero(sb, sizeof(struct vinfo_stat)); |
1272 | sb->vst_mode = pinfo->pshm_mode; | |
1273 | sb->vst_uid = pinfo->pshm_uid; | |
1274 | sb->vst_gid = pinfo->pshm_gid; | |
1275 | sb->vst_size = pinfo->pshm_length; | |
0c530ab8 A |
1276 | |
1277 | info->pshm_mappaddr = pshm->mapp_addr; | |
1278 | bcopy(&pinfo->pshm_name[0], &info->pshm_name[0], PSHMNAMLEN+1); | |
1279 | ||
1280 | PSHM_SUBSYS_UNLOCK(); | |
1281 | return(0); | |
1282 | } | |
1283 | ||
2d21ac55 A |
1284 | #if CONFIG_MACF |
1285 | void | |
1286 | pshm_label_associate(struct fileproc *fp, struct vnode *vp, vfs_context_t ctx) | |
1287 | { | |
1288 | struct pshmnode *pnode; | |
1289 | struct pshminfo *pshm; | |
0c530ab8 | 1290 | |
2d21ac55 A |
1291 | PSHM_SUBSYS_LOCK(); |
1292 | pnode = (struct pshmnode *)fp->f_fglob->fg_data; | |
1293 | if (pnode != NULL) { | |
1294 | pshm = pnode->pinfo; | |
1295 | if (pshm != NULL) | |
1296 | mac_posixshm_vnode_label_associate( | |
1297 | vfs_context_ucred(ctx), pshm, pshm->pshm_label, | |
1298 | vp, vp->v_label); | |
1299 | } | |
1300 | PSHM_SUBSYS_UNLOCK(); | |
1301 | } | |
1302 | #endif |