]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/posix_sem.c
9569311a69cf8383c20ca79715b42488fb645f34
[apple/xnu.git] / bsd / kern / posix_sem.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * Copyright (c) 1990, 1996-1998 Apple Computer, Inc.
24 * All Rights Reserved.
25 */
26 /*
27 * posix_shm.c : Support for POSIX semaphore APIs
28 *
29 * File: posix_sem.c
30 * Author: Ananthakrishna Ramesh
31 *
32 * HISTORY
33 * 2-Sep-1999 A.Ramesh
34 * Created for MacOSX
35 *
36 */
37
38 #include <sys/cdefs.h>
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/file_internal.h>
43 #include <sys/filedesc.h>
44 #include <sys/stat.h>
45 #include <sys/proc_internal.h>
46 #include <sys/kauth.h>
47 #include <sys/mount.h>
48 #include <sys/namei.h>
49 #include <sys/vnode.h>
50 #include <sys/ioctl.h>
51 #include <sys/tty.h>
52 #include <sys/malloc.h>
53 #include <sys/semaphore.h>
54 #include <sys/sysproto.h>
55
56 #include <bsm/audit_kernel.h>
57
58 #include <mach/mach_types.h>
59 #include <mach/vm_prot.h>
60 #include <mach/semaphore.h>
61 #include <mach/sync_policy.h>
62 #include <mach/task.h>
63 #include <kern/kern_types.h>
64 #include <kern/task.h>
65 #include <kern/clock.h>
66 #include <mach/kern_return.h>
67
68 #if KTRACE
69 #include <sys/ktrace.h>
70 #endif
71
72 #define f_flag f_fglob->fg_flag
73 #define f_type f_fglob->fg_type
74 #define f_msgcount f_fglob->fg_msgcount
75 #define f_cred f_fglob->fg_cred
76 #define f_ops f_fglob->fg_ops
77 #define f_offset f_fglob->fg_offset
78 #define f_data f_fglob->fg_data
79 #define PSEMNAMLEN 31 /* maximum name segment length we bother with */
80
81 struct pseminfo {
82 unsigned int psem_flags;
83 unsigned int psem_usecount;
84 mode_t psem_mode;
85 uid_t psem_uid;
86 gid_t psem_gid;
87 char psem_name[PSEMNAMLEN + 1]; /* segment name */
88 semaphore_t psem_semobject;
89 struct proc * sem_proc;
90 };
91 #define PSEMINFO_NULL (struct pseminfo *)0
92
93 #define PSEM_NONE 1
94 #define PSEM_DEFINED 2
95 #define PSEM_ALLOCATED 4
96 #define PSEM_MAPPED 8
97 #define PSEM_INUSE 0x10
98 #define PSEM_REMOVED 0x20
99 #define PSEM_INCREATE 0x40
100 #define PSEM_INDELETE 0x80
101
102 struct psemcache {
103 LIST_ENTRY(psemcache) psem_hash; /* hash chain */
104 struct pseminfo *pseminfo; /* vnode the name refers to */
105 int psem_nlen; /* length of name */
106 char psem_name[PSEMNAMLEN + 1]; /* segment name */
107 };
108 #define PSEMCACHE_NULL (struct psemcache *)0
109
110 struct psemstats {
111 long goodhits; /* hits that we can really use */
112 long neghits; /* negative hits that we can use */
113 long badhits; /* hits we must drop */
114 long falsehits; /* hits with id mismatch */
115 long miss; /* misses */
116 long longnames; /* long names that ignore cache */
117 };
118
119 struct psemname {
120 char *psem_nameptr; /* pointer to looked up name */
121 long psem_namelen; /* length of looked up component */
122 u_long psem_hash; /* hash value of looked up name */
123 };
124
125 struct psemnode {
126 struct pseminfo *pinfo;
127 #if DIAGNOSTIC
128 unsigned int readcnt;
129 unsigned int writecnt;
130 #endif
131 };
132 #define PSEMNODE_NULL (struct psemnode *)0
133
134
135 #define PSEMHASH(pnp) \
136 (&psemhashtbl[(pnp)->psem_hash & psemhash])
137 LIST_HEAD(psemhashhead, psemcache) *psemhashtbl; /* Hash Table */
138 u_long psemhash; /* size of hash table - 1 */
139 long psemnument; /* number of cache entries allocated */
140 long posix_sem_max = 10000; /* tunable for max POSIX semaphores */
141 /* 10000 limits to ~1M of memory */
142 SYSCTL_NODE(_kern, KERN_POSIX, posix, CTLFLAG_RW, 0, "Posix");
143 SYSCTL_NODE(_kern_posix, OID_AUTO, sem, CTLFLAG_RW, 0, "Semaphores");
144 SYSCTL_INT (_kern_posix_sem, OID_AUTO, max, CTLFLAG_RW, &posix_sem_max, 0, "max");
145
146 struct psemstats psemstats; /* cache effectiveness statistics */
147
148 static int psem_access(struct pseminfo *pinfo, int mode, kauth_cred_t cred);
149 static int psem_cache_search(struct pseminfo **,
150 struct psemname *, struct psemcache **);
151 static int psem_delete(struct pseminfo * pinfo);
152
153 static int psem_read (struct fileproc *fp, struct uio *uio,
154 kauth_cred_t cred, int flags, struct proc *p);
155 static int psem_write (struct fileproc *fp, struct uio *uio,
156 kauth_cred_t cred, int flags, struct proc *p);
157 static int psem_ioctl (struct fileproc *fp, u_long com,
158 caddr_t data, struct proc *p);
159 static int psem_select (struct fileproc *fp, int which, void *wql, struct proc *p);
160 static int psem_closefile (struct fileglob *fp, struct proc *p);
161
162 static int psem_kqfilter (struct fileproc *fp, struct knote *kn, struct proc *p);
163
164 struct fileops psemops =
165 { psem_read, psem_write, psem_ioctl, psem_select, psem_closefile, psem_kqfilter, 0 };
166
167
168 static lck_grp_t *psx_sem_subsys_lck_grp;
169 static lck_grp_attr_t *psx_sem_subsys_lck_grp_attr;
170 static lck_attr_t *psx_sem_subsys_lck_attr;
171 static lck_mtx_t psx_sem_subsys_mutex;
172
173 #define PSEM_SUBSYS_LOCK() lck_mtx_lock(& psx_sem_subsys_mutex)
174 #define PSEM_SUBSYS_UNLOCK() lck_mtx_unlock(& psx_sem_subsys_mutex)
175
176
177 static int psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *pcp);
178 /* Initialize the mutex governing access to the posix sem subsystem */
179 __private_extern__ void
180 psem_lock_init( void )
181 {
182
183 psx_sem_subsys_lck_grp_attr = lck_grp_attr_alloc_init();
184 lck_grp_attr_setstat(psx_sem_subsys_lck_grp_attr);
185
186 psx_sem_subsys_lck_grp = lck_grp_alloc_init("posix shared memory", psx_sem_subsys_lck_grp_attr);
187
188 psx_sem_subsys_lck_attr = lck_attr_alloc_init();
189 /* lck_attr_setdebug(psx_sem_subsys_lck_attr); */
190 lck_mtx_init(& psx_sem_subsys_mutex, psx_sem_subsys_lck_grp, psx_sem_subsys_lck_attr);
191 }
192
193 /*
194 * Lookup an entry in the cache
195 *
196 *
197 * status of -1 is returned if matches
198 * If the lookup determines that the name does not exist
199 * (negative cacheing), a status of ENOENT is returned. If the lookup
200 * fails, a status of zero is returned.
201 */
202
203 static int
204 psem_cache_search(psemp, pnp, pcache)
205 struct pseminfo **psemp;
206 struct psemname *pnp;
207 struct psemcache **pcache;
208 {
209 struct psemcache *pcp, *nnp;
210 struct psemhashhead *pcpp;
211
212 if (pnp->psem_namelen > PSEMNAMLEN) {
213 psemstats.longnames++;
214 return (0);
215 }
216
217 pcpp = PSEMHASH(pnp);
218 for (pcp = pcpp->lh_first; pcp != 0; pcp = nnp) {
219 nnp = pcp->psem_hash.le_next;
220 if (pcp->psem_nlen == pnp->psem_namelen &&
221 !bcmp(pcp->psem_name, pnp->psem_nameptr, (u_int)pcp-> psem_nlen))
222 break;
223 }
224
225 if (pcp == 0) {
226 psemstats.miss++;
227 return (0);
228 }
229
230 /* We found a "positive" match, return the vnode */
231 if (pcp->pseminfo) {
232 psemstats.goodhits++;
233 /* TOUCH(ncp); */
234 *psemp = pcp->pseminfo;
235 *pcache = pcp;
236 return (-1);
237 }
238
239 /*
240 * We found a "negative" match, ENOENT notifies client of this match.
241 * The nc_vpid field records whether this is a whiteout.
242 */
243 psemstats.neghits++;
244 return (ENOENT);
245 }
246
247 /*
248 * Add an entry to the cache.
249 */
250 static int
251 psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *pcp)
252 {
253 struct psemhashhead *pcpp;
254 struct pseminfo *dpinfo;
255 struct psemcache *dpcp;
256
257 #if DIAGNOSTIC
258 if (pnp->psem_namelen > NCHNAMLEN)
259 panic("cache_enter: name too long");
260 #endif
261
262
263 /* if the entry has already been added by some one else return */
264 if (psem_cache_search(&dpinfo, pnp, &dpcp) == -1) {
265 return(EEXIST);
266 }
267 if (psemnument >= posix_sem_max)
268 return(ENOSPC);
269 psemnument++;
270 /*
271 * Fill in cache info, if vp is NULL this is a "negative" cache entry.
272 * For negative entries, we have to record whether it is a whiteout.
273 * the whiteout flag is stored in the nc_vpid field which is
274 * otherwise unused.
275 */
276 pcp->pseminfo = psemp;
277 pcp->psem_nlen = pnp->psem_namelen;
278 bcopy(pnp->psem_nameptr, pcp->psem_name, (unsigned)pcp->psem_nlen);
279 pcpp = PSEMHASH(pnp);
280 #if DIAGNOSTIC
281 {
282 struct psemcache *p;
283
284 for (p = pcpp->lh_first; p != 0; p = p->psem_hash.le_next)
285 if (p == pcp)
286 panic("psem:cache_enter duplicate");
287 }
288 #endif
289 LIST_INSERT_HEAD(pcpp, pcp, psem_hash);
290 return(0);
291 }
292
293 /*
294 * Name cache initialization, from vfs_init() when we are booting
295 */
296 void
297 psem_cache_init(void)
298 {
299 psemhashtbl = hashinit(desiredvnodes, M_SHM, &psemhash);
300 }
301
302 static void
303 psem_cache_delete(struct psemcache *pcp)
304 {
305 #if DIAGNOSTIC
306 if (pcp->psem_hash.le_prev == 0)
307 panic("psem namecache purge le_prev");
308 if (pcp->psem_hash.le_next == pcp)
309 panic("namecache purge le_next");
310 #endif /* DIAGNOSTIC */
311 LIST_REMOVE(pcp, psem_hash);
312 pcp->psem_hash.le_prev = 0;
313 psemnument--;
314 }
315
316 #if NOT_USED
317 /*
318 * Invalidate a all entries to particular vnode.
319 *
320 * We actually just increment the v_id, that will do it. The entries will
321 * be purged by lookup as they get found. If the v_id wraps around, we
322 * need to ditch the entire cache, to avoid confusion. No valid vnode will
323 * ever have (v_id == 0).
324 */
325 static void
326 psem_cache_purge(void)
327 {
328 struct psemcache *pcp;
329 struct psemhashhead *pcpp;
330
331 for (pcpp = &psemhashtbl[psemhash]; pcpp >= psemhashtbl; pcpp--) {
332 while ( (pcp = pcpp->lh_first) )
333 psem_cache_delete(pcp);
334 }
335 }
336 #endif /* NOT_USED */
337
338 int
339 sem_open(struct proc *p, struct sem_open_args *uap, user_addr_t *retval)
340 {
341 struct fileproc *fp;
342 size_t i;
343 struct fileproc *nfp;
344 int indx, error;
345 struct psemname nd;
346 struct pseminfo *pinfo;
347 struct psemcache *pcp;
348 char * pnbuf;
349 char * nameptr;
350 char * cp;
351 size_t pathlen, plen;
352 int fmode ;
353 int cmode = uap->mode;
354 int value = uap->value;
355 int incache = 0;
356 struct psemnode * pnode = PSEMNODE_NULL;
357 struct psemcache * pcache = PSEMCACHE_NULL;
358 kern_return_t kret = KERN_SUCCESS;
359 int pinfo_alloc = 0;
360
361 AUDIT_ARG(fflags, uap->oflag);
362 AUDIT_ARG(mode, uap->mode);
363 AUDIT_ARG(value, uap->value);
364
365 pinfo = PSEMINFO_NULL;
366
367 MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
368 if (pnbuf == NULL)
369 return(ENOSPC);
370
371 pathlen = MAXPATHLEN;
372 error = copyinstr(uap->name, pnbuf, MAXPATHLEN, &pathlen);
373 if (error) {
374 goto bad;
375 }
376 AUDIT_ARG(text, pnbuf);
377 if ( (pathlen > PSEMNAMLEN) ) {
378 error = ENAMETOOLONG;
379 goto bad;
380 }
381
382 #ifdef PSXSEM_NAME_RESTRICT
383 nameptr = pnbuf;
384 if (*nameptr == '/') {
385 while (*(nameptr++) == '/') {
386 plen--;
387 error = EINVAL;
388 goto bad;
389 }
390 } else {
391 error = EINVAL;
392 goto bad;
393 }
394 #endif /* PSXSEM_NAME_RESTRICT */
395
396 plen = pathlen;
397 nameptr = pnbuf;
398 nd.psem_nameptr = nameptr;
399 nd.psem_namelen = plen;
400 nd. psem_hash =0;
401
402 for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) {
403 nd.psem_hash += (unsigned char)*cp * i;
404 }
405
406 #if KTRACE
407 if (KTRPOINT(p, KTR_NAMEI))
408 ktrnamei(p->p_tracep, nameptr);
409 #endif
410
411 PSEM_SUBSYS_LOCK();
412 error = psem_cache_search(&pinfo, &nd, &pcache);
413
414 if (error == ENOENT) {
415 PSEM_SUBSYS_UNLOCK();
416 error = EINVAL;
417 goto bad;
418
419 }
420 if (!error) {
421 incache = 0;
422 } else
423 incache = 1;
424 fmode = FFLAGS(uap->oflag);
425
426 PSEM_SUBSYS_UNLOCK();
427 error = falloc(p, &nfp, &indx);
428 if (error)
429 goto bad;
430
431 PSEM_SUBSYS_LOCK();
432 fp = nfp;
433 cmode &= ALLPERMS;
434
435 if (((fmode & (O_CREAT | O_EXCL))==(O_CREAT | O_EXCL)) && incache) {
436 /* sem exists and opened O_EXCL */
437 #if notyet
438 if (pinfo->psem_flags & PSEM_INDELETE) {
439 }
440 #endif
441 AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid,
442 pinfo->psem_gid, pinfo->psem_mode);
443 PSEM_SUBSYS_UNLOCK();
444 error = EEXIST;
445 goto bad1;
446 }
447 if (((fmode & (O_CREAT | O_EXCL))== O_CREAT) && incache) {
448 /* As per POSIX, O_CREAT has no effect */
449 fmode &= ~O_CREAT;
450 }
451
452 if ( (fmode & O_CREAT) ) {
453 if((value < 0) && (value > SEM_VALUE_MAX)) {
454 PSEM_SUBSYS_UNLOCK();
455 error = EINVAL;
456 goto bad1;
457 }
458 PSEM_SUBSYS_UNLOCK();
459 MALLOC(pinfo, struct pseminfo *, sizeof(struct pseminfo), M_SHM, M_WAITOK|M_ZERO);
460 if (pinfo == NULL) {
461 error = ENOSPC;
462 goto bad1;
463 }
464 PSEM_SUBSYS_LOCK();
465
466 pinfo_alloc = 1;
467 pinfo->psem_flags = PSEM_DEFINED | PSEM_INCREATE;
468 pinfo->psem_usecount = 1;
469 pinfo->psem_mode = cmode;
470 pinfo->psem_uid = kauth_cred_getuid(kauth_cred_get());
471 pinfo->psem_gid = kauth_cred_get()->cr_gid;
472 PSEM_SUBSYS_UNLOCK();
473 kret = semaphore_create(kernel_task, &pinfo->psem_semobject,
474 SYNC_POLICY_FIFO, value);
475 if(kret != KERN_SUCCESS)
476 goto bad3;
477 PSEM_SUBSYS_LOCK();
478 pinfo->psem_flags &= ~PSEM_DEFINED;
479 pinfo->psem_flags |= PSEM_ALLOCATED;
480 pinfo->sem_proc = p;
481 } else {
482 /* semaphore should exist as it is without O_CREAT */
483 if (!incache) {
484 PSEM_SUBSYS_UNLOCK();
485 error = ENOENT;
486 goto bad1;
487 }
488 if( pinfo->psem_flags & PSEM_INDELETE) {
489 PSEM_SUBSYS_UNLOCK();
490 error = ENOENT;
491 goto bad1;
492 }
493 AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid,
494 pinfo->psem_gid, pinfo->psem_mode);
495 if ( (error = psem_access(pinfo, fmode, kauth_cred_get())) ) {
496 PSEM_SUBSYS_UNLOCK();
497 goto bad1;
498 }
499 }
500 PSEM_SUBSYS_UNLOCK();
501 MALLOC(pnode, struct psemnode *, sizeof(struct psemnode), M_SHM, M_WAITOK|M_ZERO);
502 if (pnode == NULL) {
503 error = ENOSPC;
504 goto bad1;
505 }
506 if (!incache) {
507 /*
508 * We allocate a new entry if we are less than the maximum
509 * allowed and the one at the front of the LRU list is in use.
510 * Otherwise we use the one at the front of the LRU list.
511 */
512 MALLOC(pcp, struct psemcache *, sizeof(struct psemcache), M_SHM, M_WAITOK|M_ZERO);
513 if (pcp == NULL) {
514 error = ENOMEM;
515 goto bad2;
516 }
517
518 }
519 PSEM_SUBSYS_LOCK();
520 if (!incache) {
521 if ( (error = psem_cache_add(pinfo, &nd, pcp)) ) {
522 PSEM_SUBSYS_UNLOCK();
523 FREE(pcp, M_SHM);
524 goto bad2;
525 }
526 }
527 pinfo->psem_flags &= ~PSEM_INCREATE;
528 pinfo->psem_usecount++;
529 pnode->pinfo = pinfo;
530 PSEM_SUBSYS_UNLOCK();
531
532 proc_fdlock(p);
533 fp->f_flag = fmode & FMASK;
534 fp->f_type = DTYPE_PSXSEM;
535 fp->f_ops = &psemops;
536 fp->f_data = (caddr_t)pnode;
537 procfdtbl_releasefd(p, indx, NULL);
538 fp_drop(p, indx, fp, 1);
539 proc_fdunlock(p);
540
541 *retval = CAST_USER_ADDR_T(indx);
542 FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI);
543 return (0);
544
545 bad3:
546 switch (kret) {
547 case KERN_RESOURCE_SHORTAGE:
548 error = ENOMEM;
549 case KERN_PROTECTION_FAILURE:
550 error = EACCES;
551 default:
552 error = EINVAL;
553 }
554 goto bad1;
555 bad2:
556 FREE(pnode, M_SHM);
557 bad1:
558 if (pinfo_alloc)
559 FREE(pinfo, M_SHM);
560 fp_free(p, indx, nfp);
561 bad:
562 FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI);
563 return (error);
564 }
565
566 /*
567 * XXX This code is repeated in several places
568 */
569 static int
570 psem_access(struct pseminfo *pinfo, int mode, kauth_cred_t cred)
571 {
572 mode_t mask;
573 int is_member;
574
575 /* Otherwise, user id 0 always gets access. */
576 if (!suser(cred, NULL))
577 return (0);
578
579 mask = 0;
580
581 /* Otherwise, check the owner. */
582 if (kauth_cred_getuid(cred) == pinfo->psem_uid) {
583 if (mode & FREAD)
584 mask |= S_IRUSR;
585 if (mode & FWRITE)
586 mask |= S_IWUSR;
587 return ((pinfo->psem_mode & mask) == mask ? 0 : EACCES);
588 }
589
590 /* Otherwise, check the groups. */
591 if (kauth_cred_ismember_gid(cred, pinfo->psem_gid, &is_member) == 0 && is_member) {
592 if (mode & FREAD)
593 mask |= S_IRGRP;
594 if (mode & FWRITE)
595 mask |= S_IWGRP;
596 return ((pinfo->psem_mode & mask) == mask ? 0 : EACCES);
597 }
598
599 /* Otherwise, check everyone else. */
600 if (mode & FREAD)
601 mask |= S_IROTH;
602 if (mode & FWRITE)
603 mask |= S_IWOTH;
604 return ((pinfo->psem_mode & mask) == mask ? 0 : EACCES);
605 }
606
607 int
608 sem_unlink(__unused struct proc *p, struct sem_unlink_args *uap, __unused register_t *retval)
609 {
610 size_t i;
611 int error=0;
612 struct psemname nd;
613 struct pseminfo *pinfo;
614 char * pnbuf;
615 char * nameptr;
616 char * cp;
617 size_t pathlen, plen;
618 int incache = 0;
619 struct psemcache *pcache = PSEMCACHE_NULL;
620
621 pinfo = PSEMINFO_NULL;
622
623 MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
624 if (pnbuf == NULL) {
625 return(ENOSPC); /* XXX non-standard */
626 }
627 pathlen = MAXPATHLEN;
628 error = copyinstr(uap->name, pnbuf, MAXPATHLEN, &pathlen);
629 if (error) {
630 goto bad;
631 }
632 AUDIT_ARG(text, pnbuf);
633 if (pathlen > PSEMNAMLEN) {
634 error = ENAMETOOLONG;
635 goto bad;
636 }
637
638
639 #ifdef PSXSEM_NAME_RESTRICT
640 nameptr = pnbuf;
641 if (*nameptr == '/') {
642 while (*(nameptr++) == '/') {
643 plen--;
644 error = EINVAL;
645 goto bad;
646 }
647 } else {
648 error = EINVAL;
649 goto bad;
650 }
651 #endif /* PSXSEM_NAME_RESTRICT */
652
653 plen = pathlen;
654 nameptr = pnbuf;
655 nd.psem_nameptr = nameptr;
656 nd.psem_namelen = plen;
657 nd. psem_hash =0;
658
659 for (cp = nameptr, i=1; *cp != 0 && i <= plen; i++, cp++) {
660 nd.psem_hash += (unsigned char)*cp * i;
661 }
662
663 PSEM_SUBSYS_LOCK();
664 error = psem_cache_search(&pinfo, &nd, &pcache);
665
666 if (error == ENOENT) {
667 PSEM_SUBSYS_UNLOCK();
668 error = EINVAL;
669 goto bad;
670
671 }
672 if (!error) {
673 PSEM_SUBSYS_UNLOCK();
674 error = EINVAL;
675 goto bad;
676 } else
677 incache = 1;
678 if ( (error = psem_access(pinfo, pinfo->psem_mode, kauth_cred_get())) ) {
679 PSEM_SUBSYS_UNLOCK();
680 goto bad;
681 }
682
683 if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED))==0) {
684 PSEM_SUBSYS_UNLOCK();
685 return (EINVAL);
686 }
687
688 if ( (pinfo->psem_flags & PSEM_INDELETE) ) {
689 PSEM_SUBSYS_UNLOCK();
690 error = 0;
691 goto bad;
692 }
693
694 AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, pinfo->psem_gid,
695 pinfo->psem_mode);
696
697 pinfo->psem_flags |= PSEM_INDELETE;
698 pinfo->psem_usecount--;
699
700 if (!pinfo->psem_usecount) {
701 psem_delete(pinfo);
702 FREE(pinfo,M_SHM);
703 } else
704 pinfo->psem_flags |= PSEM_REMOVED;
705
706 psem_cache_delete(pcache);
707 PSEM_SUBSYS_UNLOCK();
708 FREE(pcache, M_SHM);
709 error = 0;
710 bad:
711 FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI);
712 return (error);
713 }
714
715 int
716 sem_close(struct proc *p, struct sem_close_args *uap, __unused register_t *retval)
717 {
718 int fd = CAST_DOWN(int,uap->sem);
719 struct fileproc *fp;
720 int error = 0;
721
722 AUDIT_ARG(fd, fd); /* XXX This seems wrong; uap->sem is a pointer */
723
724 proc_fdlock(p);
725 error = fp_lookup(p,fd, &fp, 1);
726 if (error) {
727 proc_fdunlock(p);
728 return(error);
729 }
730 fdrelse(p, fd);
731 error = closef_locked(fp, fp->f_fglob, p);
732 FREE_ZONE(fp, sizeof *fp, M_FILEPROC);
733 proc_fdunlock(p);
734 return(error);
735 }
736
737 int
738 sem_wait(struct proc *p, struct sem_wait_args *uap, __unused register_t *retval)
739 {
740 int fd = CAST_DOWN(int,uap->sem);
741 struct fileproc *fp;
742 struct pseminfo * pinfo;
743 struct psemnode * pnode ;
744 kern_return_t kret;
745 int error;
746
747 error = fp_getfpsem(p, fd, &fp, &pnode);
748 if (error)
749 return (error);
750 if (((pnode = (struct psemnode *)fp->f_data)) == PSEMNODE_NULL ) {
751 error = EINVAL;
752 goto out;
753 }
754 PSEM_SUBSYS_LOCK();
755 if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) {
756 PSEM_SUBSYS_UNLOCK();
757 error = EINVAL;
758 goto out;
759 }
760 if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED))
761 != PSEM_ALLOCATED) {
762 PSEM_SUBSYS_UNLOCK();
763 error = EINVAL;
764 goto out;
765 }
766
767 PSEM_SUBSYS_UNLOCK();
768 kret = semaphore_wait(pinfo->psem_semobject);
769 switch (kret) {
770 case KERN_INVALID_ADDRESS:
771 case KERN_PROTECTION_FAILURE:
772 error = EACCES;
773 break;
774 case KERN_ABORTED:
775 case KERN_OPERATION_TIMED_OUT:
776 error = EINTR;
777 break;
778 case KERN_SUCCESS:
779 error = 0;
780 break;
781 default:
782 error = EINVAL;
783 break;
784 }
785 out:
786 fp_drop(p, fd, fp, 0);
787 return(error);
788
789 }
790
791 int
792 sem_trywait(struct proc *p, struct sem_trywait_args *uap, __unused register_t *retval)
793 {
794 int fd = CAST_DOWN(int,uap->sem);
795 struct fileproc *fp;
796 struct pseminfo * pinfo;
797 struct psemnode * pnode ;
798 kern_return_t kret;
799 mach_timespec_t wait_time;
800 int error;
801
802 error = fp_getfpsem(p, fd, &fp, &pnode);
803 if (error)
804 return (error);
805 if (((pnode = (struct psemnode *)fp->f_data)) == PSEMNODE_NULL ) {
806 error = EINVAL;
807 goto out;
808 }
809 PSEM_SUBSYS_LOCK();
810 if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) {
811 PSEM_SUBSYS_UNLOCK();
812 error = EINVAL;
813 goto out;
814 }
815 if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED))
816 != PSEM_ALLOCATED) {
817 PSEM_SUBSYS_UNLOCK();
818 error = EINVAL;
819 goto out;
820 }
821
822 PSEM_SUBSYS_UNLOCK();
823 wait_time.tv_sec = 0;
824 wait_time.tv_nsec = 0;
825
826 kret = semaphore_timedwait(pinfo->psem_semobject, MACH_TIMESPEC_ZERO);
827 switch (kret) {
828 case KERN_INVALID_ADDRESS:
829 case KERN_PROTECTION_FAILURE:
830 error = EINVAL;
831 break;
832 case KERN_ABORTED:
833 error = EINTR;
834 break;
835 case KERN_OPERATION_TIMED_OUT:
836 error = EAGAIN;
837 break;
838 case KERN_SUCCESS:
839 error = 0;
840 break;
841 default:
842 error = EINVAL;
843 break;
844 }
845 out:
846 fp_drop(p, fd, fp, 0);
847 return(error);
848 }
849
850 int
851 sem_post(struct proc *p, struct sem_post_args *uap, __unused register_t *retval)
852 {
853 int fd = CAST_DOWN(int,uap->sem);
854 struct fileproc *fp;
855 struct pseminfo * pinfo;
856 struct psemnode * pnode ;
857 kern_return_t kret;
858 int error;
859
860 error = fp_getfpsem(p, fd, &fp, &pnode);
861 if (error)
862 return (error);
863 if (((pnode = (struct psemnode *)fp->f_data)) == PSEMNODE_NULL ) {
864 error = EINVAL;
865 goto out;
866 }
867 PSEM_SUBSYS_LOCK();
868 if ((pinfo = pnode->pinfo) == PSEMINFO_NULL) {
869 PSEM_SUBSYS_UNLOCK();
870 error = EINVAL;
871 goto out;
872 }
873 if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED))
874 != PSEM_ALLOCATED) {
875 PSEM_SUBSYS_UNLOCK();
876 error = EINVAL;
877 goto out;
878 }
879
880 PSEM_SUBSYS_UNLOCK();
881 kret = semaphore_signal(pinfo->psem_semobject);
882 switch (kret) {
883 case KERN_INVALID_ADDRESS:
884 case KERN_PROTECTION_FAILURE:
885 error = EINVAL;
886 break;
887 case KERN_ABORTED:
888 case KERN_OPERATION_TIMED_OUT:
889 error = EINTR;
890 break;
891 case KERN_SUCCESS:
892 error = 0;
893 break;
894 default:
895 error = EINVAL;
896 break;
897 }
898 out:
899 fp_drop(p, fd, fp, 0);
900 return(error);
901 }
902
903 int
904 sem_init(__unused struct proc *p, __unused struct sem_init_args *uap, __unused register_t *retval)
905 {
906 return(ENOSYS);
907 }
908
909 int
910 sem_destroy(__unused struct proc *p, __unused struct sem_destroy_args *uap, __unused register_t *retval)
911 {
912 return(ENOSYS);
913 }
914
915 int
916 sem_getvalue(__unused struct proc *p, __unused struct sem_getvalue_args *uap, __unused register_t *retval)
917 {
918 return(ENOSYS);
919 }
920
921 static int
922 psem_close(struct psemnode *pnode, __unused int flags,
923 __unused kauth_cred_t cred, __unused struct proc *p)
924 {
925 int error=0;
926 register struct pseminfo *pinfo;
927
928 PSEM_SUBSYS_LOCK();
929 if ((pinfo = pnode->pinfo) == PSEMINFO_NULL){
930 PSEM_SUBSYS_UNLOCK();
931 return(EINVAL);
932 }
933
934 if ((pinfo->psem_flags & PSEM_ALLOCATED) != PSEM_ALLOCATED) {
935 PSEM_SUBSYS_UNLOCK();
936 return(EINVAL);
937 }
938 #if DIAGNOSTIC
939 if(!pinfo->psem_usecount) {
940 kprintf("negative usecount in psem_close\n");
941 }
942 #endif /* DIAGNOSTIC */
943 pinfo->psem_usecount--;
944
945 if ((pinfo->psem_flags & PSEM_REMOVED) && !pinfo->psem_usecount) {
946 PSEM_SUBSYS_UNLOCK();
947 /* lock dropped as only semaphore is destroyed here */
948 error = psem_delete(pinfo);
949 FREE(pinfo,M_SHM);
950 } else {
951 PSEM_SUBSYS_UNLOCK();
952 }
953 /* subsystem lock is dropped when we get here */
954 FREE(pnode, M_SHM);
955 return (error);
956 }
957
958 static int
959 psem_closefile(fg, p)
960 struct fileglob *fg;
961 struct proc *p;
962 {
963 int error;
964
965 /* Not locked as psem_close is called only from here and is locked properly */
966 error = psem_close(((struct psemnode *)fg->fg_data), fg->fg_flag,
967 fg->fg_cred, p);
968
969 return(error);
970 }
971
972 static int
973 psem_delete(struct pseminfo * pinfo)
974 {
975 kern_return_t kret;
976
977 kret = semaphore_destroy(kernel_task, pinfo->psem_semobject);
978
979 switch (kret) {
980 case KERN_INVALID_ADDRESS:
981 case KERN_PROTECTION_FAILURE:
982 return (EINVAL);
983 case KERN_ABORTED:
984 case KERN_OPERATION_TIMED_OUT:
985 return (EINTR);
986 case KERN_SUCCESS:
987 return(0);
988 default:
989 return (EINVAL);
990 }
991 }
992
993 static int
994 psem_read(__unused struct fileproc *fp, __unused struct uio *uio,
995 __unused kauth_cred_t cred, __unused int flags,
996 __unused struct proc *p)
997 {
998 return(ENOTSUP);
999 }
1000
1001 static int
1002 psem_write(__unused struct fileproc *fp, __unused struct uio *uio,
1003 __unused kauth_cred_t cred, __unused int flags,
1004 __unused struct proc *p)
1005 {
1006 return(ENOTSUP);
1007 }
1008
1009 static int
1010 psem_ioctl(__unused struct fileproc *fp, __unused u_long com,
1011 __unused caddr_t data, __unused struct proc *p)
1012 {
1013 return(ENOTSUP);
1014 }
1015
1016 static int
1017 psem_select(__unused struct fileproc *fp, __unused int which,
1018 __unused void *wql, __unused struct proc *p)
1019 {
1020 return(ENOTSUP);
1021 }
1022
1023 static int
1024 psem_kqfilter(__unused struct fileproc *fp, __unused struct knote *kn,
1025 __unused struct proc *p)
1026 {
1027 return (ENOTSUP);
1028 }
1029