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