]>
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@ |
0a7de745 | 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. | |
0a7de745 | 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. | |
0a7de745 | 17 | * |
2d21ac55 A |
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. | |
0a7de745 | 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 | /* | |
6d2010ae | 33 | * posix_sem.c : Support for POSIX semaphore APIs |
1c79356b A |
34 | * |
35 | * File: posix_sem.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> | |
62 | #include <sys/ioctl.h> | |
63 | #include <sys/tty.h> | |
64 | #include <sys/malloc.h> | |
65 | #include <sys/semaphore.h> | |
91447636 | 66 | #include <sys/sysproto.h> |
0c530ab8 | 67 | #include <sys/proc_info.h> |
e5568f75 | 68 | |
2d21ac55 A |
69 | #if CONFIG_MACF |
70 | #include <sys/vnode_internal.h> | |
71 | #include <security/mac_framework.h> | |
72 | #endif | |
73 | ||
b0d623f7 | 74 | #include <security/audit/audit.h> |
e5568f75 | 75 | |
1c79356b A |
76 | #include <mach/mach_types.h> |
77 | #include <mach/vm_prot.h> | |
78 | #include <mach/semaphore.h> | |
79 | #include <mach/sync_policy.h> | |
91447636 A |
80 | #include <mach/task.h> |
81 | #include <kern/kern_types.h> | |
1c79356b A |
82 | #include <kern/task.h> |
83 | #include <kern/clock.h> | |
84 | #include <mach/kern_return.h> | |
85 | ||
f427ee49 A |
86 | #define f_flag fp_glob->fg_flag |
87 | #define f_ops fp_glob->fg_ops | |
88 | #define f_data fp_glob->fg_data | |
91447636 | 89 | |
0a7de745 | 90 | #define PSEMNAMLEN 31 /* maximum name segment length we bother with */ |
1c79356b A |
91 | |
92 | struct pseminfo { | |
0a7de745 A |
93 | unsigned int psem_flags; |
94 | unsigned int psem_usecount; | |
95 | mode_t psem_mode; | |
96 | uid_t psem_uid; | |
97 | gid_t psem_gid; | |
98 | char psem_name[PSEMNAMLEN + 1]; /* segment name */ | |
99 | semaphore_t psem_semobject; | |
100 | struct label * psem_label; | |
101 | pid_t psem_creator_pid; | |
102 | uint64_t psem_creator_uniqueid; | |
1c79356b A |
103 | }; |
104 | #define PSEMINFO_NULL (struct pseminfo *)0 | |
105 | ||
0a7de745 A |
106 | #define PSEM_NONE 1 |
107 | #define PSEM_DEFINED 2 | |
108 | #define PSEM_ALLOCATED 4 | |
109 | #define PSEM_MAPPED 8 | |
110 | #define PSEM_INUSE 0x10 | |
111 | #define PSEM_REMOVED 0x20 | |
112 | #define PSEM_INCREATE 0x40 | |
113 | #define PSEM_INDELETE 0x80 | |
114 | ||
115 | struct psemcache { | |
116 | LIST_ENTRY(psemcache) psem_hash; /* hash chain */ | |
117 | struct pseminfo *pseminfo; /* vnode the name refers to */ | |
f427ee49 | 118 | size_t psem_nlen; /* length of name */ |
0a7de745 | 119 | char psem_name[PSEMNAMLEN + 1]; /* segment name */ |
1c79356b A |
120 | }; |
121 | #define PSEMCACHE_NULL (struct psemcache *)0 | |
122 | ||
490019cf A |
123 | #define PSEMCACHE_NOTFOUND (0) |
124 | #define PSEMCACHE_FOUND (-1) | |
125 | #define PSEMCACHE_NEGATIVE (ENOENT) | |
126 | ||
0a7de745 A |
127 | struct psemstats { |
128 | long goodhits; /* hits that we can really use */ | |
129 | long neghits; /* negative hits that we can use */ | |
130 | long badhits; /* hits we must drop */ | |
131 | long falsehits; /* hits with id mismatch */ | |
132 | long miss; /* misses */ | |
133 | long longnames; /* long names that ignore cache */ | |
1c79356b A |
134 | }; |
135 | ||
136 | struct psemname { | |
0a7de745 | 137 | char *psem_nameptr; /* pointer to looked up name */ |
f427ee49 | 138 | size_t psem_namelen; /* length of looked up component */ |
0a7de745 | 139 | u_int32_t psem_hash; /* hash value of looked up name */ |
1c79356b A |
140 | }; |
141 | ||
142 | struct psemnode { | |
143 | struct pseminfo *pinfo; | |
144 | #if DIAGNOSTIC | |
145 | unsigned int readcnt; | |
146 | unsigned int writecnt; | |
147 | #endif | |
148 | }; | |
149 | #define PSEMNODE_NULL (struct psemnode *)0 | |
150 | ||
151 | ||
152 | #define PSEMHASH(pnp) \ | |
153 | (&psemhashtbl[(pnp)->psem_hash & psemhash]) | |
0a7de745 A |
154 | LIST_HEAD(psemhashhead, psemcache) * psemhashtbl; /* Hash Table */ |
155 | u_long psemhash; /* size of hash table - 1 */ | |
156 | long psemnument; /* number of cache entries allocated */ | |
157 | long posix_sem_max = 10000; /* tunable for max POSIX semaphores */ | |
158 | /* 10000 limits to ~1M of memory */ | |
6d2010ae A |
159 | SYSCTL_NODE(_kern, KERN_POSIX, posix, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "Posix"); |
160 | SYSCTL_NODE(_kern_posix, OID_AUTO, sem, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "Semaphores"); | |
0a7de745 | 161 | SYSCTL_LONG(_kern_posix_sem, OID_AUTO, max, CTLFLAG_RW | CTLFLAG_LOCKED, &posix_sem_max, "max"); |
91447636 | 162 | |
0a7de745 | 163 | struct psemstats psemstats; /* cache effectiveness statistics */ |
1c79356b | 164 | |
f427ee49 | 165 | static int psem_access(struct pseminfo *pinfo, mode_t mode, kauth_cred_t cred); |
91447636 | 166 | static int psem_cache_search(struct pseminfo **, |
0a7de745 | 167 | struct psemname *, struct psemcache **); |
91447636 | 168 | static int psem_delete(struct pseminfo * pinfo); |
1c79356b | 169 | |
0a7de745 | 170 | static int psem_closefile(struct fileglob *fp, vfs_context_t ctx); |
490019cf | 171 | static int psem_unlink_internal(struct pseminfo *pinfo, struct psemcache *pcache); |
1c79356b | 172 | |
39236c6e | 173 | static const struct fileops psemops = { |
cb323159 A |
174 | .fo_type = DTYPE_PSXSEM, |
175 | .fo_read = fo_no_read, | |
176 | .fo_write = fo_no_write, | |
177 | .fo_ioctl = fo_no_ioctl, | |
178 | .fo_select = fo_no_select, | |
179 | .fo_close = psem_closefile, | |
180 | .fo_drain = fo_no_drain, | |
181 | .fo_kqfilter = fo_no_kqfilter, | |
39236c6e | 182 | }; |
91447636 | 183 | |
c3c9b80d A |
184 | static LCK_GRP_DECLARE(psx_sem_subsys_lck_grp, "posix semaphores"); |
185 | static LCK_MTX_DECLARE(psx_sem_subsys_mutex, &psx_sem_subsys_lck_grp); | |
91447636 | 186 | |
c3c9b80d A |
187 | #define PSEM_SUBSYS_LOCK() lck_mtx_lock(&psx_sem_subsys_mutex) |
188 | #define PSEM_SUBSYS_UNLOCK() lck_mtx_unlock(&psx_sem_subsys_mutex) | |
39037602 | 189 | #define PSEM_SUBSYS_ASSERT_HELD() LCK_MTX_ASSERT(&psx_sem_subsys_mutex, LCK_MTX_ASSERT_OWNED) |
91447636 A |
190 | |
191 | ||
192 | static int psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *pcp); | |
490019cf A |
193 | static void psem_cache_delete(struct psemcache *pcp); |
194 | int psem_cache_purge_all(proc_t); | |
195 | ||
1c79356b | 196 | /* |
0a7de745 A |
197 | * Lookup an entry in the cache |
198 | * | |
199 | * | |
1c79356b A |
200 | * status of -1 is returned if matches |
201 | * If the lookup determines that the name does not exist | |
202 | * (negative cacheing), a status of ENOENT is returned. If the lookup | |
203 | * fails, a status of zero is returned. | |
204 | */ | |
205 | ||
9bccf70c | 206 | static int |
2d21ac55 | 207 | psem_cache_search(struct pseminfo **psemp, struct psemname *pnp, |
0a7de745 | 208 | struct psemcache **pcache) |
1c79356b | 209 | { |
91447636 A |
210 | struct psemcache *pcp, *nnp; |
211 | struct psemhashhead *pcpp; | |
1c79356b A |
212 | |
213 | if (pnp->psem_namelen > PSEMNAMLEN) { | |
214 | psemstats.longnames++; | |
490019cf | 215 | return PSEMCACHE_NOTFOUND; |
1c79356b A |
216 | } |
217 | ||
218 | pcpp = PSEMHASH(pnp); | |
219 | for (pcp = pcpp->lh_first; pcp != 0; pcp = nnp) { | |
220 | nnp = pcp->psem_hash.le_next; | |
221 | if (pcp->psem_nlen == pnp->psem_namelen && | |
f427ee49 | 222 | !bcmp(pcp->psem_name, pnp->psem_nameptr, pcp->psem_nlen)) { |
1c79356b | 223 | break; |
0a7de745 | 224 | } |
1c79356b A |
225 | } |
226 | ||
227 | if (pcp == 0) { | |
228 | psemstats.miss++; | |
490019cf | 229 | return PSEMCACHE_NOTFOUND; |
1c79356b A |
230 | } |
231 | ||
232 | /* We found a "positive" match, return the vnode */ | |
0a7de745 | 233 | if (pcp->pseminfo) { |
1c79356b A |
234 | psemstats.goodhits++; |
235 | /* TOUCH(ncp); */ | |
236 | *psemp = pcp->pseminfo; | |
237 | *pcache = pcp; | |
490019cf | 238 | return PSEMCACHE_FOUND; |
1c79356b A |
239 | } |
240 | ||
241 | /* | |
242 | * We found a "negative" match, ENOENT notifies client of this match. | |
243 | * The nc_vpid field records whether this is a whiteout. | |
244 | */ | |
245 | psemstats.neghits++; | |
490019cf | 246 | return PSEMCACHE_NEGATIVE; |
1c79356b A |
247 | } |
248 | ||
249 | /* | |
250 | * Add an entry to the cache. | |
251 | */ | |
9bccf70c | 252 | static int |
91447636 | 253 | psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *pcp) |
1c79356b | 254 | { |
91447636 | 255 | struct psemhashhead *pcpp; |
1c79356b A |
256 | struct pseminfo *dpinfo; |
257 | struct psemcache *dpcp; | |
258 | ||
259 | #if DIAGNOSTIC | |
0a7de745 | 260 | if (pnp->psem_namelen > PSEMNAMLEN) { |
1c79356b | 261 | panic("cache_enter: name too long"); |
0a7de745 | 262 | } |
1c79356b A |
263 | #endif |
264 | ||
91447636 | 265 | |
1c79356b | 266 | /* if the entry has already been added by some one else return */ |
490019cf A |
267 | if (psem_cache_search(&dpinfo, pnp, &dpcp) == PSEMCACHE_FOUND) { |
268 | return EEXIST; | |
1c79356b | 269 | } |
0a7de745 | 270 | if (psemnument >= posix_sem_max) { |
490019cf | 271 | return ENOSPC; |
0a7de745 | 272 | } |
1c79356b | 273 | psemnument++; |
1c79356b A |
274 | /* |
275 | * Fill in cache info, if vp is NULL this is a "negative" cache entry. | |
276 | * For negative entries, we have to record whether it is a whiteout. | |
277 | * the whiteout flag is stored in the nc_vpid field which is | |
278 | * otherwise unused. | |
279 | */ | |
280 | pcp->pseminfo = psemp; | |
281 | pcp->psem_nlen = pnp->psem_namelen; | |
f427ee49 | 282 | bcopy(pnp->psem_nameptr, pcp->psem_name, pcp->psem_nlen); |
1c79356b A |
283 | pcpp = PSEMHASH(pnp); |
284 | #if DIAGNOSTIC | |
285 | { | |
91447636 | 286 | struct psemcache *p; |
1c79356b | 287 | |
0a7de745 A |
288 | for (p = pcpp->lh_first; p != 0; p = p->psem_hash.le_next) { |
289 | if (p == pcp) { | |
1c79356b | 290 | panic("psem:cache_enter duplicate"); |
0a7de745 A |
291 | } |
292 | } | |
1c79356b A |
293 | } |
294 | #endif | |
295 | LIST_INSERT_HEAD(pcpp, pcp, psem_hash); | |
490019cf | 296 | return 0; |
1c79356b A |
297 | } |
298 | ||
299 | /* | |
300 | * Name cache initialization, from vfs_init() when we are booting | |
301 | */ | |
302 | void | |
91447636 | 303 | psem_cache_init(void) |
1c79356b | 304 | { |
f427ee49 | 305 | psemhashtbl = hashinit((int)(posix_sem_max / 2), M_SHM, &psemhash); |
1c79356b A |
306 | } |
307 | ||
9bccf70c | 308 | static void |
91447636 | 309 | psem_cache_delete(struct psemcache *pcp) |
9bccf70c A |
310 | { |
311 | #if DIAGNOSTIC | |
0a7de745 | 312 | if (pcp->psem_hash.le_prev == 0) { |
9bccf70c | 313 | panic("psem namecache purge le_prev"); |
0a7de745 A |
314 | } |
315 | if (pcp->psem_hash.le_next == pcp) { | |
9bccf70c | 316 | panic("namecache purge le_next"); |
0a7de745 | 317 | } |
9bccf70c A |
318 | #endif /* DIAGNOSTIC */ |
319 | LIST_REMOVE(pcp, psem_hash); | |
0a7de745 | 320 | pcp->psem_hash.le_prev = NULL; |
9bccf70c A |
321 | psemnument--; |
322 | } | |
323 | ||
1c79356b | 324 | /* |
490019cf A |
325 | * Remove all cached psem entries. Open semaphores (with a positive refcount) |
326 | * will continue to exist, but their cache entries tying them to a particular | |
327 | * name/path will be removed making all future lookups on the name fail. | |
1c79356b | 328 | */ |
490019cf A |
329 | int |
330 | psem_cache_purge_all(__unused proc_t p) | |
1c79356b | 331 | { |
490019cf | 332 | struct psemcache *pcp, *tmppcp; |
1c79356b | 333 | struct psemhashhead *pcpp; |
490019cf | 334 | int error = 0; |
1c79356b | 335 | |
0a7de745 | 336 | if (kauth_cred_issuser(kauth_cred_get()) == 0) { |
490019cf | 337 | return EPERM; |
0a7de745 | 338 | } |
490019cf A |
339 | |
340 | PSEM_SUBSYS_LOCK(); | |
1c79356b | 341 | for (pcpp = &psemhashtbl[psemhash]; pcpp >= psemhashtbl; pcpp--) { |
490019cf A |
342 | LIST_FOREACH_SAFE(pcp, pcpp, psem_hash, tmppcp) { |
343 | assert(pcp->psem_nlen); | |
344 | /* | |
345 | * unconditionally unlink the cache entry | |
346 | */ | |
347 | error = psem_unlink_internal(pcp->pseminfo, pcp); | |
0a7de745 | 348 | if (error) { |
490019cf | 349 | goto out; |
0a7de745 | 350 | } |
490019cf | 351 | } |
1c79356b | 352 | } |
490019cf A |
353 | assert(psemnument == 0); |
354 | ||
355 | out: | |
356 | PSEM_SUBSYS_UNLOCK(); | |
357 | ||
0a7de745 | 358 | if (error) { |
490019cf | 359 | printf("%s: Error %d removing all semaphores: %ld remain!\n", |
0a7de745 A |
360 | __func__, error, psemnument); |
361 | } | |
490019cf | 362 | return error; |
1c79356b | 363 | } |
1c79356b | 364 | |
f427ee49 A |
365 | /* |
366 | * In order to support unnamed POSIX semaphores, the named | |
367 | * POSIX semaphores will have to move out of the per-process | |
368 | * open filetable, and into a global table that is shared with | |
369 | * unnamed POSIX semaphores, since unnamed POSIX semaphores | |
370 | * are typically used by declaring instances in shared memory, | |
371 | * and there's no other way to do this without changing the | |
372 | * underlying type, which would introduce binary compatibility | |
373 | * issues. | |
374 | */ | |
1c79356b | 375 | int |
2d21ac55 | 376 | sem_open(proc_t p, struct sem_open_args *uap, user_addr_t *retval) |
1c79356b | 377 | { |
91447636 | 378 | size_t i; |
91447636 | 379 | int indx, error; |
1c79356b A |
380 | struct psemname nd; |
381 | struct pseminfo *pinfo; | |
b0d623f7 A |
382 | struct fileproc *fp = NULL; |
383 | char *pnbuf = NULL; | |
384 | struct pseminfo *new_pinfo = PSEMINFO_NULL; | |
385 | struct psemnode *new_pnode = PSEMNODE_NULL; | |
386 | struct psemcache *pcache = PSEMCACHE_NULL; | |
1c79356b A |
387 | char * nameptr; |
388 | char * cp; | |
389 | size_t pathlen, plen; | |
f427ee49 A |
390 | mode_t fmode; |
391 | mode_t cmode = (mode_t)uap->mode; | |
1c79356b A |
392 | int value = uap->value; |
393 | int incache = 0; | |
b0d623f7 | 394 | struct psemcache *pcp = PSEMCACHE_NULL; |
0a7de745 A |
395 | kern_return_t kret = KERN_INVALID_ADDRESS; /* default fail */ |
396 | ||
e5568f75 | 397 | AUDIT_ARG(fflags, uap->oflag); |
f427ee49 | 398 | AUDIT_ARG(mode, (mode_t)uap->mode); |
b0d623f7 | 399 | AUDIT_ARG(value32, uap->value); |
91447636 | 400 | |
1c79356b A |
401 | pinfo = PSEMINFO_NULL; |
402 | ||
b0d623f7 A |
403 | /* |
404 | * Preallocate everything we might need up front to avoid taking | |
405 | * and dropping the lock, opening us up to race conditions. | |
406 | */ | |
f427ee49 | 407 | pnbuf = zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO); |
91447636 | 408 | |
1c79356b | 409 | pathlen = MAXPATHLEN; |
91447636 | 410 | error = copyinstr(uap->name, pnbuf, MAXPATHLEN, &pathlen); |
1c79356b A |
411 | if (error) { |
412 | goto bad; | |
413 | } | |
e5568f75 | 414 | AUDIT_ARG(text, pnbuf); |
0a7de745 | 415 | if ((pathlen > PSEMNAMLEN)) { |
1c79356b A |
416 | error = ENAMETOOLONG; |
417 | goto bad; | |
418 | } | |
419 | ||
1c79356b A |
420 | #ifdef PSXSEM_NAME_RESTRICT |
421 | nameptr = pnbuf; | |
422 | if (*nameptr == '/') { | |
423 | while (*(nameptr++) == '/') { | |
424 | plen--; | |
425 | error = EINVAL; | |
426 | goto bad; | |
427 | } | |
0a7de745 | 428 | } else { |
1c79356b A |
429 | error = EINVAL; |
430 | goto bad; | |
431 | } | |
432 | #endif /* PSXSEM_NAME_RESTRICT */ | |
433 | ||
434 | plen = pathlen; | |
435 | nameptr = pnbuf; | |
436 | nd.psem_nameptr = nameptr; | |
437 | nd.psem_namelen = plen; | |
2d21ac55 | 438 | nd.psem_hash = 0; |
1c79356b | 439 | |
0a7de745 A |
440 | for (cp = nameptr, i = 1; *cp != 0 && i <= plen; i++, cp++) { |
441 | nd.psem_hash += (unsigned char)*cp * i; | |
1c79356b A |
442 | } |
443 | ||
b0d623f7 A |
444 | /* |
445 | * attempt to allocate a new fp; if unsuccessful, the fp will be | |
446 | * left unmodified (NULL). | |
447 | */ | |
448 | error = falloc(p, &fp, &indx, vfs_context_current()); | |
0a7de745 | 449 | if (error) { |
b0d623f7 | 450 | goto bad; |
0a7de745 | 451 | } |
b0d623f7 A |
452 | |
453 | /* | |
454 | * We allocate a new entry if we are less than the maximum | |
455 | * allowed and the one at the front of the LRU list is in use. | |
456 | * Otherwise we use the one at the front of the LRU list. | |
457 | */ | |
c3c9b80d | 458 | pcp = kheap_alloc(KM_SHM, sizeof(struct psemcache), Z_WAITOK | Z_ZERO); |
b0d623f7 A |
459 | if (pcp == PSEMCACHE_NULL) { |
460 | error = ENOMEM; | |
461 | goto bad; | |
462 | } | |
463 | ||
c3c9b80d | 464 | new_pinfo = kheap_alloc(KM_SHM, sizeof(struct pseminfo), Z_WAITOK | Z_ZERO); |
b0d623f7 A |
465 | if (new_pinfo == NULL) { |
466 | error = ENOSPC; | |
467 | goto bad; | |
468 | } | |
469 | #if CONFIG_MACF | |
470 | mac_posixsem_label_init(new_pinfo); | |
471 | #endif | |
472 | ||
473 | /* | |
474 | * Provisionally create the semaphore in the new_pinfo; we have to do | |
475 | * this here to prevent locking later. We use the value of kret to | |
476 | * signal success or failure, which is why we set its default value | |
477 | * to KERN_INVALID_ADDRESS, above. | |
478 | */ | |
0a7de745 | 479 | |
f427ee49 | 480 | fmode = (mode_t)FFLAGS(uap->oflag); |
0a7de745 A |
481 | |
482 | if ((fmode & O_CREAT)) { | |
483 | if ((value < 0) || (value > SEM_VALUE_MAX)) { | |
b0d623f7 A |
484 | error = EINVAL; |
485 | goto bad; | |
486 | } | |
0a7de745 | 487 | |
b0d623f7 | 488 | kret = semaphore_create(kernel_task, &new_pinfo->psem_semobject, SYNC_POLICY_FIFO, value); |
0a7de745 | 489 | |
b0d623f7 A |
490 | if (kret != KERN_SUCCESS) { |
491 | switch (kret) { | |
0a7de745 A |
492 | case KERN_RESOURCE_SHORTAGE: |
493 | error = ENOMEM; | |
494 | break; | |
495 | case KERN_PROTECTION_FAILURE: | |
496 | error = EACCES; | |
497 | break; | |
498 | default: | |
499 | error = EINVAL; | |
b0d623f7 A |
500 | } |
501 | goto bad; | |
502 | } | |
503 | } | |
0a7de745 | 504 | |
c3c9b80d | 505 | new_pnode = kheap_alloc(KM_SHM, sizeof(struct psemnode), Z_WAITOK | Z_ZERO); |
b0d623f7 A |
506 | if (new_pnode == NULL) { |
507 | error = ENOSPC; | |
508 | goto bad; | |
509 | } | |
510 | ||
91447636 | 511 | PSEM_SUBSYS_LOCK(); |
1c79356b A |
512 | error = psem_cache_search(&pinfo, &nd, &pcache); |
513 | ||
490019cf | 514 | if (error == PSEMCACHE_NEGATIVE) { |
1c79356b | 515 | error = EINVAL; |
b0d623f7 | 516 | goto bad_locked; |
1c79356b | 517 | } |
490019cf | 518 | |
0a7de745 | 519 | if (error == PSEMCACHE_FOUND) { |
1c79356b | 520 | incache = 1; |
0a7de745 | 521 | } else { |
490019cf | 522 | incache = 0; |
0a7de745 | 523 | } |
1c79356b | 524 | |
1c79356b A |
525 | cmode &= ALLPERMS; |
526 | ||
0a7de745 | 527 | if (((fmode & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) && incache) { |
1c79356b A |
528 | /* sem exists and opened O_EXCL */ |
529 | #if notyet | |
530 | if (pinfo->psem_flags & PSEM_INDELETE) { | |
531 | } | |
0a7de745 | 532 | #endif |
e5568f75 | 533 | AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, |
0a7de745 | 534 | pinfo->psem_gid, pinfo->psem_mode); |
1c79356b | 535 | error = EEXIST; |
b0d623f7 | 536 | goto bad_locked; |
1c79356b | 537 | } |
0a7de745 | 538 | if (((fmode & (O_CREAT | O_EXCL)) == O_CREAT) && incache) { |
1c79356b A |
539 | /* As per POSIX, O_CREAT has no effect */ |
540 | fmode &= ~O_CREAT; | |
541 | } | |
542 | ||
0a7de745 | 543 | if ((fmode & O_CREAT)) { |
b0d623f7 A |
544 | /* create a new one (commit the allocation) */ |
545 | pinfo = new_pinfo; | |
1c79356b A |
546 | pinfo->psem_flags = PSEM_DEFINED | PSEM_INCREATE; |
547 | pinfo->psem_usecount = 1; | |
548 | pinfo->psem_mode = cmode; | |
6d2010ae A |
549 | pinfo->psem_uid = kauth_getuid(); |
550 | pinfo->psem_gid = kauth_getgid(); | |
0c530ab8 | 551 | bcopy(pnbuf, &pinfo->psem_name[0], PSEMNAMLEN); |
0a7de745 | 552 | pinfo->psem_name[PSEMNAMLEN] = 0; |
1c79356b A |
553 | pinfo->psem_flags &= ~PSEM_DEFINED; |
554 | pinfo->psem_flags |= PSEM_ALLOCATED; | |
39236c6e A |
555 | pinfo->psem_creator_pid = p->p_pid; |
556 | pinfo->psem_creator_uniqueid = p->p_uniqueid; | |
0a7de745 | 557 | |
2d21ac55 A |
558 | #if CONFIG_MACF |
559 | error = mac_posixsem_check_create(kauth_cred_get(), nameptr); | |
560 | if (error) { | |
b0d623f7 | 561 | goto bad_locked; |
2d21ac55 A |
562 | } |
563 | mac_posixsem_label_associate(kauth_cred_get(), pinfo, nameptr); | |
564 | #endif | |
1c79356b A |
565 | } else { |
566 | /* semaphore should exist as it is without O_CREAT */ | |
567 | if (!incache) { | |
568 | error = ENOENT; | |
b0d623f7 | 569 | goto bad_locked; |
1c79356b | 570 | } |
0a7de745 | 571 | if (pinfo->psem_flags & PSEM_INDELETE) { |
1c79356b | 572 | error = ENOENT; |
b0d623f7 | 573 | goto bad_locked; |
0a7de745 | 574 | } |
e5568f75 | 575 | AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, |
0a7de745 | 576 | pinfo->psem_gid, pinfo->psem_mode); |
2d21ac55 A |
577 | #if CONFIG_MACF |
578 | error = mac_posixsem_check_open(kauth_cred_get(), pinfo); | |
579 | if (error) { | |
b0d623f7 | 580 | goto bad_locked; |
2d21ac55 A |
581 | } |
582 | #endif | |
0a7de745 | 583 | if ((error = psem_access(pinfo, fmode, kauth_cred_get()))) { |
b0d623f7 | 584 | goto bad_locked; |
91447636 | 585 | } |
1c79356b | 586 | } |
1c79356b A |
587 | |
588 | if (!incache) { | |
b0d623f7 | 589 | /* if successful, this will consume the pcp */ |
0a7de745 | 590 | if ((error = psem_cache_add(pinfo, &nd, pcp))) { |
b0d623f7 | 591 | goto bad_locked; |
1c79356b A |
592 | } |
593 | } | |
594 | pinfo->psem_flags &= ~PSEM_INCREATE; | |
595 | pinfo->psem_usecount++; | |
b0d623f7 | 596 | new_pnode->pinfo = pinfo; |
91447636 A |
597 | PSEM_SUBSYS_UNLOCK(); |
598 | ||
b0d623f7 A |
599 | /* |
600 | * if incache, we did not use the new pcp or the new pcp or the | |
601 | * new . and we must free them. | |
602 | */ | |
603 | if (incache) { | |
c3c9b80d | 604 | kheap_free(KM_SHM, pcp, sizeof(struct psemcache)); |
b0d623f7 A |
605 | pcp = PSEMCACHE_NULL; |
606 | if (new_pinfo != PSEMINFO_NULL) { | |
607 | /* return value ignored - we can't _not_ do this */ | |
608 | (void)semaphore_destroy(kernel_task, new_pinfo->psem_semobject); | |
609 | #if CONFIG_MACF | |
610 | mac_posixsem_label_destroy(new_pinfo); | |
611 | #endif | |
c3c9b80d | 612 | kheap_free(KM_SHM, new_pinfo, sizeof(struct pseminfo)); |
b0d623f7 A |
613 | new_pinfo = PSEMINFO_NULL; |
614 | } | |
615 | } | |
616 | ||
91447636 | 617 | proc_fdlock(p); |
55e303ae | 618 | fp->f_flag = fmode & FMASK; |
1c79356b | 619 | fp->f_ops = &psemops; |
b0d623f7 | 620 | fp->f_data = (caddr_t)new_pnode; |
6601e61a | 621 | procfdtbl_releasefd(p, indx, NULL); |
91447636 A |
622 | fp_drop(p, indx, fp, 1); |
623 | proc_fdunlock(p); | |
624 | ||
625 | *retval = CAST_USER_ADDR_T(indx); | |
f427ee49 | 626 | zfree(ZV_NAMEI, pnbuf); |
0a7de745 | 627 | return 0; |
1c79356b | 628 | |
b0d623f7 A |
629 | bad_locked: |
630 | PSEM_SUBSYS_UNLOCK(); | |
631 | bad: | |
c3c9b80d | 632 | kheap_free(KM_SHM, pcp, sizeof(struct psemcache)); |
b0d623f7 | 633 | |
c3c9b80d | 634 | kheap_free(KM_SHM, new_pnode, sizeof(struct psemnode)); |
b0d623f7 | 635 | |
0a7de745 | 636 | if (fp != NULL) { |
b0d623f7 | 637 | fp_free(p, indx, fp); |
0a7de745 | 638 | } |
b0d623f7 A |
639 | |
640 | if (new_pinfo != PSEMINFO_NULL) { | |
641 | /* | |
642 | * kret signals whether or not we successfully created a | |
643 | * Mach semaphore for this semaphore; if so, we need to | |
644 | * destroy it here. | |
645 | */ | |
646 | if (kret == KERN_SUCCESS) { | |
647 | /* return value ignored - we can't _not_ do this */ | |
648 | (void)semaphore_destroy(kernel_task, new_pinfo->psem_semobject); | |
649 | } | |
2d21ac55 | 650 | #if CONFIG_MACF |
b0d623f7 | 651 | mac_posixsem_label_destroy(new_pinfo); |
2d21ac55 | 652 | #endif |
c3c9b80d | 653 | kheap_free(KM_SHM, new_pinfo, sizeof(struct pseminfo)); |
2d21ac55 | 654 | } |
b0d623f7 | 655 | |
0a7de745 | 656 | if (pnbuf != NULL) { |
f427ee49 | 657 | zfree(ZV_NAMEI, pnbuf); |
0a7de745 A |
658 | } |
659 | return error; | |
1c79356b A |
660 | } |
661 | ||
91447636 A |
662 | /* |
663 | * XXX This code is repeated in several places | |
664 | */ | |
665 | static int | |
f427ee49 | 666 | psem_access(struct pseminfo *pinfo, mode_t mode, kauth_cred_t cred) |
1c79356b | 667 | { |
f427ee49 | 668 | mode_t mode_req = ((mode & FREAD) ? S_IRUSR : 0) | |
0a7de745 | 669 | ((mode & FWRITE) ? S_IWUSR : 0); |
1c79356b A |
670 | |
671 | /* Otherwise, user id 0 always gets access. */ | |
0a7de745 A |
672 | if (!suser(cred, NULL)) { |
673 | return 0; | |
674 | } | |
1c79356b | 675 | |
0a7de745 | 676 | return posix_cred_access(cred, pinfo->psem_uid, pinfo->psem_gid, pinfo->psem_mode, mode_req); |
1c79356b A |
677 | } |
678 | ||
490019cf A |
679 | static int |
680 | psem_unlink_internal(struct pseminfo *pinfo, struct psemcache *pcache) | |
681 | { | |
682 | PSEM_SUBSYS_ASSERT_HELD(); | |
683 | ||
0a7de745 | 684 | if (!pinfo || !pcache) { |
490019cf | 685 | return EINVAL; |
0a7de745 | 686 | } |
490019cf | 687 | |
0a7de745 | 688 | if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED)) == 0) { |
490019cf | 689 | return EINVAL; |
0a7de745 | 690 | } |
490019cf | 691 | |
0a7de745 | 692 | if (pinfo->psem_flags & PSEM_INDELETE) { |
490019cf | 693 | return 0; |
0a7de745 | 694 | } |
490019cf A |
695 | |
696 | AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, pinfo->psem_gid, | |
0a7de745 | 697 | pinfo->psem_mode); |
490019cf A |
698 | |
699 | pinfo->psem_flags |= PSEM_INDELETE; | |
700 | pinfo->psem_usecount--; | |
701 | ||
702 | if (!pinfo->psem_usecount) { | |
703 | psem_delete(pinfo); | |
c3c9b80d | 704 | kheap_free(KM_SHM, pinfo, sizeof(struct pseminfo)); |
490019cf A |
705 | } else { |
706 | pinfo->psem_flags |= PSEM_REMOVED; | |
707 | } | |
708 | ||
709 | psem_cache_delete(pcache); | |
c3c9b80d | 710 | kheap_free(KM_SHM, pcache, sizeof(struct psemcache)); |
490019cf A |
711 | return 0; |
712 | } | |
713 | ||
714 | ||
1c79356b | 715 | int |
b0d623f7 | 716 | sem_unlink(__unused proc_t p, struct sem_unlink_args *uap, __unused int32_t *retval) |
1c79356b | 717 | { |
91447636 | 718 | size_t i; |
0a7de745 | 719 | int error = 0; |
1c79356b A |
720 | struct psemname nd; |
721 | struct pseminfo *pinfo; | |
1c79356b A |
722 | char * nameptr; |
723 | char * cp; | |
490019cf A |
724 | char * pnbuf; |
725 | size_t pathlen; | |
1c79356b | 726 | struct psemcache *pcache = PSEMCACHE_NULL; |
1c79356b A |
727 | |
728 | pinfo = PSEMINFO_NULL; | |
729 | ||
f427ee49 A |
730 | pnbuf = zalloc(ZV_NAMEI); |
731 | ||
1c79356b | 732 | pathlen = MAXPATHLEN; |
91447636 | 733 | error = copyinstr(uap->name, pnbuf, MAXPATHLEN, &pathlen); |
1c79356b A |
734 | if (error) { |
735 | goto bad; | |
736 | } | |
e5568f75 | 737 | AUDIT_ARG(text, pnbuf); |
1c79356b A |
738 | if (pathlen > PSEMNAMLEN) { |
739 | error = ENAMETOOLONG; | |
740 | goto bad; | |
741 | } | |
742 | ||
490019cf | 743 | nameptr = pnbuf; |
1c79356b A |
744 | |
745 | #ifdef PSXSEM_NAME_RESTRICT | |
1c79356b A |
746 | if (*nameptr == '/') { |
747 | while (*(nameptr++) == '/') { | |
490019cf | 748 | pathlen--; |
1c79356b A |
749 | error = EINVAL; |
750 | goto bad; | |
751 | } | |
0a7de745 | 752 | } else { |
1c79356b A |
753 | error = EINVAL; |
754 | goto bad; | |
755 | } | |
756 | #endif /* PSXSEM_NAME_RESTRICT */ | |
757 | ||
1c79356b | 758 | nd.psem_nameptr = nameptr; |
490019cf | 759 | nd.psem_namelen = pathlen; |
0a7de745 | 760 | nd.psem_hash = 0; |
1c79356b | 761 | |
0a7de745 A |
762 | for (cp = nameptr, i = 1; *cp != 0 && i <= pathlen; i++, cp++) { |
763 | nd.psem_hash += (unsigned char)*cp * i; | |
1c79356b A |
764 | } |
765 | ||
91447636 | 766 | PSEM_SUBSYS_LOCK(); |
1c79356b A |
767 | error = psem_cache_search(&pinfo, &nd, &pcache); |
768 | ||
490019cf | 769 | if (error != PSEMCACHE_FOUND) { |
91447636 | 770 | PSEM_SUBSYS_UNLOCK(); |
cb323159 | 771 | error = ENOENT; |
1c79356b | 772 | goto bad; |
1c79356b | 773 | } |
490019cf | 774 | |
2d21ac55 A |
775 | #if CONFIG_MACF |
776 | error = mac_posixsem_check_unlink(kauth_cred_get(), pinfo, nameptr); | |
777 | if (error) { | |
778 | PSEM_SUBSYS_UNLOCK(); | |
779 | goto bad; | |
780 | } | |
781 | #endif | |
0a7de745 | 782 | if ((error = psem_access(pinfo, pinfo->psem_mode, kauth_cred_get()))) { |
91447636 | 783 | PSEM_SUBSYS_UNLOCK(); |
1c79356b | 784 | goto bad; |
91447636 | 785 | } |
1c79356b | 786 | |
490019cf | 787 | error = psem_unlink_internal(pinfo, pcache); |
91447636 | 788 | PSEM_SUBSYS_UNLOCK(); |
490019cf | 789 | |
1c79356b | 790 | bad: |
f427ee49 | 791 | zfree(ZV_NAMEI, pnbuf); |
490019cf | 792 | return error; |
1c79356b A |
793 | } |
794 | ||
1c79356b | 795 | int |
b0d623f7 | 796 | sem_close(proc_t p, struct sem_close_args *uap, __unused int32_t *retval) |
1c79356b | 797 | { |
0a7de745 | 798 | int fd = CAST_DOWN_EXPLICIT(int, uap->sem); |
91447636 | 799 | struct fileproc *fp; |
1c79356b | 800 | |
e5568f75 | 801 | AUDIT_ARG(fd, fd); /* XXX This seems wrong; uap->sem is a pointer */ |
91447636 A |
802 | |
803 | proc_fdlock(p); | |
f427ee49 | 804 | if ((fp = fp_get_noref_locked(p, fd)) == NULL) { |
91447636 | 805 | proc_fdunlock(p); |
f427ee49 | 806 | return EBADF; |
91447636 | 807 | } |
f427ee49 | 808 | if (FILEGLOB_DTYPE(fp->fp_glob) != DTYPE_PSXSEM) { |
e8c3f781 | 809 | proc_fdunlock(p); |
0a7de745 | 810 | return EBADF; |
e8c3f781 | 811 | } |
f427ee49 | 812 | return fp_close_and_unlock(p, fd, fp, 0); |
1c79356b A |
813 | } |
814 | ||
1c79356b | 815 | int |
b0d623f7 | 816 | sem_wait(proc_t p, struct sem_wait_args *uap, int32_t *retval) |
2d21ac55 A |
817 | { |
818 | __pthread_testcancel(1); | |
0a7de745 | 819 | return sem_wait_nocancel(p, (struct sem_wait_nocancel_args *)uap, retval); |
2d21ac55 A |
820 | } |
821 | ||
822 | int | |
b0d623f7 | 823 | sem_wait_nocancel(proc_t p, struct sem_wait_nocancel_args *uap, __unused int32_t *retval) |
1c79356b | 824 | { |
0a7de745 | 825 | int fd = CAST_DOWN_EXPLICIT(int, uap->sem); |
91447636 | 826 | struct fileproc *fp; |
1c79356b | 827 | struct pseminfo * pinfo; |
0a7de745 | 828 | struct psemnode * pnode; |
1c79356b A |
829 | kern_return_t kret; |
830 | int error; | |
831 | ||
f427ee49 | 832 | error = fp_get_ftype(p, fd, DTYPE_PSXSEM, EBADF, &fp); |
0a7de745 A |
833 | if (error) { |
834 | return error; | |
835 | } | |
f427ee49 A |
836 | pnode = (struct psemnode *)fp->f_data; |
837 | ||
91447636 A |
838 | PSEM_SUBSYS_LOCK(); |
839 | if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) { | |
840 | PSEM_SUBSYS_UNLOCK(); | |
841 | error = EINVAL; | |
842 | goto out; | |
843 | } | |
0a7de745 A |
844 | if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED)) |
845 | != PSEM_ALLOCATED) { | |
91447636 A |
846 | PSEM_SUBSYS_UNLOCK(); |
847 | error = EINVAL; | |
848 | goto out; | |
1c79356b | 849 | } |
2d21ac55 A |
850 | #if CONFIG_MACF |
851 | error = mac_posixsem_check_wait(kauth_cred_get(), pinfo); | |
852 | if (error) { | |
853 | PSEM_SUBSYS_UNLOCK(); | |
854 | goto out; | |
855 | } | |
856 | #endif | |
91447636 | 857 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
858 | kret = semaphore_wait(pinfo->psem_semobject); |
859 | switch (kret) { | |
860 | case KERN_INVALID_ADDRESS: | |
861 | case KERN_PROTECTION_FAILURE: | |
91447636 A |
862 | error = EACCES; |
863 | break; | |
1c79356b A |
864 | case KERN_ABORTED: |
865 | case KERN_OPERATION_TIMED_OUT: | |
91447636 A |
866 | error = EINTR; |
867 | break; | |
1c79356b | 868 | case KERN_SUCCESS: |
91447636 A |
869 | error = 0; |
870 | break; | |
1c79356b | 871 | default: |
91447636 A |
872 | error = EINVAL; |
873 | break; | |
1c79356b | 874 | } |
91447636 A |
875 | out: |
876 | fp_drop(p, fd, fp, 0); | |
0a7de745 | 877 | return error; |
91447636 | 878 | } |
1c79356b A |
879 | |
880 | int | |
b0d623f7 | 881 | sem_trywait(proc_t p, struct sem_trywait_args *uap, __unused int32_t *retval) |
1c79356b | 882 | { |
0a7de745 | 883 | int fd = CAST_DOWN_EXPLICIT(int, uap->sem); |
91447636 | 884 | struct fileproc *fp; |
1c79356b | 885 | struct pseminfo * pinfo; |
0a7de745 | 886 | struct psemnode * pnode; |
1c79356b A |
887 | kern_return_t kret; |
888 | mach_timespec_t wait_time; | |
889 | int error; | |
0a7de745 | 890 | |
f427ee49 | 891 | error = fp_get_ftype(p, fd, DTYPE_PSXSEM, EBADF, &fp); |
0a7de745 A |
892 | if (error) { |
893 | return error; | |
894 | } | |
f427ee49 A |
895 | pnode = (struct psemnode *)fp->f_data; |
896 | ||
91447636 A |
897 | PSEM_SUBSYS_LOCK(); |
898 | if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) { | |
899 | PSEM_SUBSYS_UNLOCK(); | |
900 | error = EINVAL; | |
901 | goto out; | |
902 | } | |
0a7de745 A |
903 | if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED)) |
904 | != PSEM_ALLOCATED) { | |
91447636 A |
905 | PSEM_SUBSYS_UNLOCK(); |
906 | error = EINVAL; | |
907 | goto out; | |
1c79356b | 908 | } |
2d21ac55 A |
909 | #if CONFIG_MACF |
910 | error = mac_posixsem_check_wait(kauth_cred_get(), pinfo); | |
911 | if (error) { | |
912 | PSEM_SUBSYS_UNLOCK(); | |
913 | goto out; | |
914 | } | |
915 | #endif | |
91447636 | 916 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
917 | wait_time.tv_sec = 0; |
918 | wait_time.tv_nsec = 0; | |
919 | ||
920 | kret = semaphore_timedwait(pinfo->psem_semobject, MACH_TIMESPEC_ZERO); | |
921 | switch (kret) { | |
922 | case KERN_INVALID_ADDRESS: | |
923 | case KERN_PROTECTION_FAILURE: | |
91447636 A |
924 | error = EINVAL; |
925 | break; | |
1c79356b | 926 | case KERN_ABORTED: |
91447636 A |
927 | error = EINTR; |
928 | break; | |
1c79356b | 929 | case KERN_OPERATION_TIMED_OUT: |
91447636 A |
930 | error = EAGAIN; |
931 | break; | |
1c79356b | 932 | case KERN_SUCCESS: |
91447636 A |
933 | error = 0; |
934 | break; | |
1c79356b | 935 | default: |
91447636 A |
936 | error = EINVAL; |
937 | break; | |
1c79356b | 938 | } |
91447636 A |
939 | out: |
940 | fp_drop(p, fd, fp, 0); | |
0a7de745 | 941 | return error; |
1c79356b A |
942 | } |
943 | ||
1c79356b | 944 | int |
b0d623f7 | 945 | sem_post(proc_t p, struct sem_post_args *uap, __unused int32_t *retval) |
1c79356b | 946 | { |
0a7de745 | 947 | int fd = CAST_DOWN_EXPLICIT(int, uap->sem); |
91447636 | 948 | struct fileproc *fp; |
1c79356b | 949 | struct pseminfo * pinfo; |
0a7de745 | 950 | struct psemnode * pnode; |
1c79356b A |
951 | kern_return_t kret; |
952 | int error; | |
953 | ||
f427ee49 | 954 | error = fp_get_ftype(p, fd, DTYPE_PSXSEM, EBADF, &fp); |
0a7de745 A |
955 | if (error) { |
956 | return error; | |
957 | } | |
f427ee49 A |
958 | pnode = (struct psemnode *)fp->f_data; |
959 | ||
91447636 A |
960 | PSEM_SUBSYS_LOCK(); |
961 | if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) { | |
962 | PSEM_SUBSYS_UNLOCK(); | |
963 | error = EINVAL; | |
964 | goto out; | |
965 | } | |
0a7de745 A |
966 | if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED)) |
967 | != PSEM_ALLOCATED) { | |
91447636 A |
968 | PSEM_SUBSYS_UNLOCK(); |
969 | error = EINVAL; | |
970 | goto out; | |
1c79356b | 971 | } |
2d21ac55 A |
972 | #if CONFIG_MACF |
973 | error = mac_posixsem_check_post(kauth_cred_get(), pinfo); | |
974 | if (error) { | |
975 | PSEM_SUBSYS_UNLOCK(); | |
976 | goto out; | |
977 | } | |
978 | #endif | |
91447636 | 979 | PSEM_SUBSYS_UNLOCK(); |
1c79356b A |
980 | kret = semaphore_signal(pinfo->psem_semobject); |
981 | switch (kret) { | |
982 | case KERN_INVALID_ADDRESS: | |
983 | case KERN_PROTECTION_FAILURE: | |
91447636 A |
984 | error = EINVAL; |
985 | break; | |
1c79356b A |
986 | case KERN_ABORTED: |
987 | case KERN_OPERATION_TIMED_OUT: | |
91447636 A |
988 | error = EINTR; |
989 | break; | |
1c79356b | 990 | case KERN_SUCCESS: |
91447636 A |
991 | error = 0; |
992 | break; | |
1c79356b | 993 | default: |
91447636 A |
994 | error = EINVAL; |
995 | break; | |
1c79356b | 996 | } |
91447636 A |
997 | out: |
998 | fp_drop(p, fd, fp, 0); | |
0a7de745 | 999 | return error; |
1c79356b A |
1000 | } |
1001 | ||
9bccf70c | 1002 | static int |
f427ee49 | 1003 | psem_close(struct psemnode *pnode) |
1c79356b | 1004 | { |
0a7de745 | 1005 | int error = 0; |
2d21ac55 | 1006 | struct pseminfo *pinfo; |
1c79356b | 1007 | |
91447636 | 1008 | PSEM_SUBSYS_LOCK(); |
0a7de745 | 1009 | if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) { |
91447636 | 1010 | PSEM_SUBSYS_UNLOCK(); |
0a7de745 | 1011 | return EINVAL; |
91447636 | 1012 | } |
1c79356b A |
1013 | |
1014 | if ((pinfo->psem_flags & PSEM_ALLOCATED) != PSEM_ALLOCATED) { | |
91447636 | 1015 | PSEM_SUBSYS_UNLOCK(); |
0a7de745 | 1016 | return EINVAL; |
1c79356b A |
1017 | } |
1018 | #if DIAGNOSTIC | |
0a7de745 | 1019 | if (!pinfo->psem_usecount) { |
1c79356b A |
1020 | kprintf("negative usecount in psem_close\n"); |
1021 | } | |
1022 | #endif /* DIAGNOSTIC */ | |
1023 | pinfo->psem_usecount--; | |
1024 | ||
0a7de745 | 1025 | if ((pinfo->psem_flags & PSEM_REMOVED) && !pinfo->psem_usecount) { |
91447636 A |
1026 | PSEM_SUBSYS_UNLOCK(); |
1027 | /* lock dropped as only semaphore is destroyed here */ | |
1c79356b | 1028 | error = psem_delete(pinfo); |
c3c9b80d | 1029 | kheap_free(KM_SHM, pinfo, sizeof(struct pseminfo)); |
91447636 A |
1030 | } else { |
1031 | PSEM_SUBSYS_UNLOCK(); | |
1c79356b | 1032 | } |
91447636 | 1033 | /* subsystem lock is dropped when we get here */ |
c3c9b80d | 1034 | kheap_free(KM_SHM, pnode, sizeof(struct psemnode)); |
0a7de745 | 1035 | return error; |
1c79356b A |
1036 | } |
1037 | ||
9bccf70c | 1038 | static int |
2d21ac55 | 1039 | psem_closefile(struct fileglob *fg, __unused vfs_context_t ctx) |
9bccf70c | 1040 | { |
2d21ac55 A |
1041 | /* |
1042 | * Not locked as psem_close is called only from here and is locked | |
1043 | * properly | |
1044 | */ | |
f427ee49 | 1045 | return psem_close((struct psemnode *)fg->fg_data); |
9bccf70c A |
1046 | } |
1047 | ||
0a7de745 | 1048 | static int |
1c79356b A |
1049 | psem_delete(struct pseminfo * pinfo) |
1050 | { | |
1051 | kern_return_t kret; | |
1052 | ||
1053 | kret = semaphore_destroy(kernel_task, pinfo->psem_semobject); | |
2d21ac55 A |
1054 | #if CONFIG_MACF |
1055 | mac_posixsem_label_destroy(pinfo); | |
1056 | #endif | |
1c79356b A |
1057 | |
1058 | switch (kret) { | |
1059 | case KERN_INVALID_ADDRESS: | |
1060 | case KERN_PROTECTION_FAILURE: | |
0a7de745 | 1061 | return EINVAL; |
1c79356b A |
1062 | case KERN_ABORTED: |
1063 | case KERN_OPERATION_TIMED_OUT: | |
0a7de745 | 1064 | return EINTR; |
1c79356b | 1065 | case KERN_SUCCESS: |
0a7de745 | 1066 | return 0; |
1c79356b | 1067 | default: |
0a7de745 | 1068 | return EINVAL; |
1c79356b | 1069 | } |
1c79356b A |
1070 | } |
1071 | ||
0c530ab8 A |
1072 | int |
1073 | fill_pseminfo(struct psemnode *pnode, struct psem_info * info) | |
1074 | { | |
2d21ac55 A |
1075 | struct pseminfo *pinfo; |
1076 | struct vinfo_stat *sb; | |
0c530ab8 A |
1077 | |
1078 | PSEM_SUBSYS_LOCK(); | |
0a7de745 | 1079 | if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) { |
0c530ab8 | 1080 | PSEM_SUBSYS_UNLOCK(); |
0a7de745 | 1081 | return EINVAL; |
0c530ab8 A |
1082 | } |
1083 | ||
1084 | #if 0 | |
1085 | if ((pinfo->psem_flags & PSEM_ALLOCATED) != PSEM_ALLOCATED) { | |
1086 | PSEM_SUBSYS_UNLOCK(); | |
0a7de745 | 1087 | return EINVAL; |
0c530ab8 A |
1088 | } |
1089 | #endif | |
1090 | ||
1091 | sb = &info->psem_stat; | |
2d21ac55 | 1092 | bzero(sb, sizeof(struct vinfo_stat)); |
0c530ab8 | 1093 | |
0a7de745 A |
1094 | sb->vst_mode = pinfo->psem_mode; |
1095 | sb->vst_uid = pinfo->psem_uid; | |
1096 | sb->vst_gid = pinfo->psem_gid; | |
1097 | sb->vst_size = pinfo->psem_usecount; | |
1098 | bcopy(&pinfo->psem_name[0], &info->psem_name[0], PSEMNAMLEN + 1); | |
0c530ab8 A |
1099 | |
1100 | PSEM_SUBSYS_UNLOCK(); | |
0a7de745 | 1101 | return 0; |
0c530ab8 A |
1102 | } |
1103 | ||
2d21ac55 A |
1104 | #if CONFIG_MACF |
1105 | void | |
1106 | psem_label_associate(struct fileproc *fp, struct vnode *vp, vfs_context_t ctx) | |
1107 | { | |
1108 | struct psemnode *pnode; | |
1109 | struct pseminfo *psem; | |
1110 | ||
1111 | PSEM_SUBSYS_LOCK(); | |
f427ee49 | 1112 | pnode = (struct psemnode *)fp->fp_glob->fg_data; |
2d21ac55 A |
1113 | if (pnode != NULL) { |
1114 | psem = pnode->pinfo; | |
0a7de745 | 1115 | if (psem != NULL) { |
2d21ac55 A |
1116 | mac_posixsem_vnode_label_associate( |
1117 | vfs_context_ucred(ctx), psem, psem->psem_label, | |
1118 | vp, vp->v_label); | |
0a7de745 | 1119 | } |
2d21ac55 A |
1120 | } |
1121 | PSEM_SUBSYS_UNLOCK(); | |
1122 | } | |
1123 | #endif |