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