]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/posix_sem.c
xnu-792.6.76.tar.gz
[apple/xnu.git] / bsd / kern / posix_sem.c
CommitLineData
1c79356b 1/*
e5568f75 2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
1c79356b
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
37839358
A
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.
1c79356b 11 *
37839358
A
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
1c79356b
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
37839358
A
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.
1c79356b
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 * Copyright (c) 1990, 1996-1998 Apple Computer, Inc.
24 * All Rights Reserved.
25 */
26/*
9bccf70c 27 * posix_shm.c : Support for POSIX semaphore APIs
1c79356b
A
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>
91447636 42#include <sys/file_internal.h>
1c79356b
A
43#include <sys/filedesc.h>
44#include <sys/stat.h>
91447636
A
45#include <sys/proc_internal.h>
46#include <sys/kauth.h>
1c79356b
A
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>
91447636 54#include <sys/sysproto.h>
e5568f75
A
55
56#include <bsm/audit_kernel.h>
57
1c79356b
A
58#include <mach/mach_types.h>
59#include <mach/vm_prot.h>
60#include <mach/semaphore.h>
61#include <mach/sync_policy.h>
91447636
A
62#include <mach/task.h>
63#include <kern/kern_types.h>
1c79356b
A
64#include <kern/task.h>
65#include <kern/clock.h>
66#include <mach/kern_return.h>
67
91447636
A
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
1c79356b
A
79#define PSEMNAMLEN 31 /* maximum name segment length we bother with */
80
81struct 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 */
91447636 88 semaphore_t psem_semobject;
1c79356b
A
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
102struct 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
110struct 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
119struct 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
125struct 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])
137LIST_HEAD(psemhashhead, psemcache) *psemhashtbl; /* Hash Table */
138u_long psemhash; /* size of hash table - 1 */
139long psemnument; /* number of cache entries allocated */
91447636
A
140long posix_sem_max = 10000; /* tunable for max POSIX semaphores */
141 /* 10000 limits to ~1M of memory */
142SYSCTL_NODE(_kern, KERN_POSIX, posix, CTLFLAG_RW, 0, "Posix");
143SYSCTL_NODE(_kern_posix, OID_AUTO, sem, CTLFLAG_RW, 0, "Semaphores");
144SYSCTL_INT (_kern_posix_sem, OID_AUTO, max, CTLFLAG_RW, &posix_sem_max, 0, "max");
145
1c79356b
A
146struct psemstats psemstats; /* cache effectiveness statistics */
147
91447636
A
148static int psem_access(struct pseminfo *pinfo, int mode, kauth_cred_t cred);
149static int psem_cache_search(struct pseminfo **,
150 struct psemname *, struct psemcache **);
151static int psem_delete(struct pseminfo * pinfo);
1c79356b 152
91447636
A
153static int psem_read (struct fileproc *fp, struct uio *uio,
154 kauth_cred_t cred, int flags, struct proc *p);
155static int psem_write (struct fileproc *fp, struct uio *uio,
156 kauth_cred_t cred, int flags, struct proc *p);
157static int psem_ioctl (struct fileproc *fp, u_long com,
158 caddr_t data, struct proc *p);
159static int psem_select (struct fileproc *fp, int which, void *wql, struct proc *p);
160static int psem_closefile (struct fileglob *fp, struct proc *p);
1c79356b 161
91447636 162static int psem_kqfilter (struct fileproc *fp, struct knote *kn, struct proc *p);
55e303ae 163
1c79356b 164struct fileops psemops =
91447636
A
165 { psem_read, psem_write, psem_ioctl, psem_select, psem_closefile, psem_kqfilter, 0 };
166
167
168static lck_grp_t *psx_sem_subsys_lck_grp;
169static lck_grp_attr_t *psx_sem_subsys_lck_grp_attr;
170static lck_attr_t *psx_sem_subsys_lck_attr;
171static 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
177static 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
180psem_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}
1c79356b
A
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
9bccf70c 203static int
1c79356b
A
204psem_cache_search(psemp, pnp, pcache)
205 struct pseminfo **psemp;
206 struct psemname *pnp;
207 struct psemcache **pcache;
208{
91447636
A
209 struct psemcache *pcp, *nnp;
210 struct psemhashhead *pcpp;
1c79356b
A
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 */
9bccf70c 250static int
91447636 251psem_cache_add(struct pseminfo *psemp, struct psemname *pnp, struct psemcache *pcp)
1c79356b 252{
91447636 253 struct psemhashhead *pcpp;
1c79356b
A
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
91447636 262
1c79356b
A
263 /* if the entry has already been added by some one else return */
264 if (psem_cache_search(&dpinfo, pnp, &dpcp) == -1) {
1c79356b
A
265 return(EEXIST);
266 }
91447636
A
267 if (psemnument >= posix_sem_max)
268 return(ENOSPC);
1c79356b 269 psemnument++;
1c79356b
A
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 {
91447636 282 struct psemcache *p;
1c79356b
A
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 */
296void
91447636 297psem_cache_init(void)
1c79356b
A
298{
299 psemhashtbl = hashinit(desiredvnodes, M_SHM, &psemhash);
300}
301
9bccf70c 302static void
91447636 303psem_cache_delete(struct psemcache *pcp)
9bccf70c
A
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
91447636 316#if NOT_USED
1c79356b
A
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 */
91447636 325static void
1c79356b
A
326psem_cache_purge(void)
327{
328 struct psemcache *pcp;
329 struct psemhashhead *pcpp;
330
331 for (pcpp = &psemhashtbl[psemhash]; pcpp >= psemhashtbl; pcpp--) {
91447636 332 while ( (pcp = pcpp->lh_first) )
1c79356b
A
333 psem_cache_delete(pcp);
334 }
335}
91447636 336#endif /* NOT_USED */
1c79356b
A
337
338int
91447636 339sem_open(struct proc *p, struct sem_open_args *uap, user_addr_t *retval)
1c79356b 340{
91447636
A
341 struct fileproc *fp;
342 size_t i;
343 struct fileproc *nfp;
344 int indx, error;
1c79356b
A
345 struct psemname nd;
346 struct pseminfo *pinfo;
91447636 347 struct psemcache *pcp;
1c79356b
A
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
e5568f75
A
361 AUDIT_ARG(fflags, uap->oflag);
362 AUDIT_ARG(mode, uap->mode);
363 AUDIT_ARG(value, uap->value);
91447636 364
1c79356b
A
365 pinfo = PSEMINFO_NULL;
366
91447636
A
367 MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
368 if (pnbuf == NULL)
369 return(ENOSPC);
370
1c79356b 371 pathlen = MAXPATHLEN;
91447636 372 error = copyinstr(uap->name, pnbuf, MAXPATHLEN, &pathlen);
1c79356b
A
373 if (error) {
374 goto bad;
375 }
e5568f75 376 AUDIT_ARG(text, pnbuf);
91447636 377 if ( (pathlen > PSEMNAMLEN) ) {
1c79356b
A
378 error = ENAMETOOLONG;
379 goto bad;
380 }
381
1c79356b
A
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
91447636
A
406#if KTRACE
407 if (KTRPOINT(p, KTR_NAMEI))
408 ktrnamei(p->p_tracep, nameptr);
409#endif
410
411 PSEM_SUBSYS_LOCK();
1c79356b
A
412 error = psem_cache_search(&pinfo, &nd, &pcache);
413
414 if (error == ENOENT) {
91447636 415 PSEM_SUBSYS_UNLOCK();
1c79356b
A
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
91447636
A
426 PSEM_SUBSYS_UNLOCK();
427 error = falloc(p, &nfp, &indx);
428 if (error)
1c79356b 429 goto bad;
1c79356b 430
91447636 431 PSEM_SUBSYS_LOCK();
1c79356b
A
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
e5568f75 441 AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid,
91447636
A
442 pinfo->psem_gid, pinfo->psem_mode);
443 PSEM_SUBSYS_UNLOCK();
1c79356b
A
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
91447636 452 if ( (fmode & O_CREAT) ) {
1c79356b 453 if((value < 0) && (value > SEM_VALUE_MAX)) {
91447636 454 PSEM_SUBSYS_UNLOCK();
1c79356b
A
455 error = EINVAL;
456 goto bad1;
457 }
91447636
A
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
1c79356b
A
466 pinfo_alloc = 1;
467 pinfo->psem_flags = PSEM_DEFINED | PSEM_INCREATE;
468 pinfo->psem_usecount = 1;
469 pinfo->psem_mode = cmode;
91447636
A
470 pinfo->psem_uid = kauth_cred_getuid(kauth_cred_get());
471 pinfo->psem_gid = kauth_cred_get()->cr_gid;
472 PSEM_SUBSYS_UNLOCK();
1c79356b
A
473 kret = semaphore_create(kernel_task, &pinfo->psem_semobject,
474 SYNC_POLICY_FIFO, value);
475 if(kret != KERN_SUCCESS)
476 goto bad3;
91447636 477 PSEM_SUBSYS_LOCK();
1c79356b
A
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) {
91447636 484 PSEM_SUBSYS_UNLOCK();
1c79356b
A
485 error = ENOENT;
486 goto bad1;
487 }
488 if( pinfo->psem_flags & PSEM_INDELETE) {
91447636 489 PSEM_SUBSYS_UNLOCK();
1c79356b
A
490 error = ENOENT;
491 goto bad1;
492 }
e5568f75 493 AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid,
91447636
A
494 pinfo->psem_gid, pinfo->psem_mode);
495 if ( (error = psem_access(pinfo, fmode, kauth_cred_get())) ) {
496 PSEM_SUBSYS_UNLOCK();
1c79356b 497 goto bad1;
91447636 498 }
1c79356b 499 }
91447636
A
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 }
1c79356b 517
91447636
A
518 }
519 PSEM_SUBSYS_LOCK();
1c79356b 520 if (!incache) {
91447636
A
521 if ( (error = psem_cache_add(pinfo, &nd, pcp)) ) {
522 PSEM_SUBSYS_UNLOCK();
523 FREE(pcp, M_SHM);
524 goto bad2;
1c79356b
A
525 }
526 }
527 pinfo->psem_flags &= ~PSEM_INCREATE;
528 pinfo->psem_usecount++;
529 pnode->pinfo = pinfo;
91447636
A
530 PSEM_SUBSYS_UNLOCK();
531
532 proc_fdlock(p);
55e303ae 533 fp->f_flag = fmode & FMASK;
1c79356b
A
534 fp->f_type = DTYPE_PSXSEM;
535 fp->f_ops = &psemops;
536 fp->f_data = (caddr_t)pnode;
537 *fdflags(p, indx) &= ~UF_RESERVED;
91447636
A
538 fp_drop(p, indx, fp, 1);
539 proc_fdunlock(p);
540
541 *retval = CAST_USER_ADDR_T(indx);
55e303ae 542 FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI);
1c79356b
A
543 return (0);
544
545bad3:
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;
555bad2:
91447636 556 FREE(pnode, M_SHM);
1c79356b 557bad1:
91447636
A
558 if (pinfo_alloc)
559 FREE(pinfo, M_SHM);
560 fp_free(p, indx, nfp);
1c79356b 561bad:
55e303ae 562 FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI);
1c79356b
A
563 return (error);
564}
565
91447636
A
566/*
567 * XXX This code is repeated in several places
568 */
569static int
570psem_access(struct pseminfo *pinfo, int mode, kauth_cred_t cred)
1c79356b
A
571{
572 mode_t mask;
91447636 573 int is_member;
1c79356b
A
574
575 /* Otherwise, user id 0 always gets access. */
91447636 576 if (!suser(cred, NULL))
1c79356b
A
577 return (0);
578
579 mask = 0;
580
581 /* Otherwise, check the owner. */
91447636 582 if (kauth_cred_getuid(cred) == pinfo->psem_uid) {
1c79356b
A
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. */
91447636
A
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 }
1c79356b
A
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
1c79356b 607int
91447636 608sem_unlink(__unused struct proc *p, struct sem_unlink_args *uap, __unused register_t *retval)
1c79356b 609{
91447636 610 size_t i;
1c79356b
A
611 int error=0;
612 struct psemname nd;
613 struct pseminfo *pinfo;
1c79356b
A
614 char * pnbuf;
615 char * nameptr;
616 char * cp;
617 size_t pathlen, plen;
1c79356b 618 int incache = 0;
1c79356b 619 struct psemcache *pcache = PSEMCACHE_NULL;
1c79356b
A
620
621 pinfo = PSEMINFO_NULL;
622
91447636
A
623 MALLOC_ZONE(pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
624 if (pnbuf == NULL) {
625 return(ENOSPC); /* XXX non-standard */
626 }
1c79356b 627 pathlen = MAXPATHLEN;
91447636 628 error = copyinstr(uap->name, pnbuf, MAXPATHLEN, &pathlen);
1c79356b
A
629 if (error) {
630 goto bad;
631 }
e5568f75 632 AUDIT_ARG(text, pnbuf);
1c79356b
A
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
91447636 663 PSEM_SUBSYS_LOCK();
1c79356b
A
664 error = psem_cache_search(&pinfo, &nd, &pcache);
665
666 if (error == ENOENT) {
91447636 667 PSEM_SUBSYS_UNLOCK();
1c79356b
A
668 error = EINVAL;
669 goto bad;
670
671 }
672 if (!error) {
91447636 673 PSEM_SUBSYS_UNLOCK();
1c79356b
A
674 error = EINVAL;
675 goto bad;
676 } else
677 incache = 1;
91447636
A
678 if ( (error = psem_access(pinfo, pinfo->psem_mode, kauth_cred_get())) ) {
679 PSEM_SUBSYS_UNLOCK();
1c79356b 680 goto bad;
91447636 681 }
1c79356b
A
682
683 if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED))==0) {
91447636 684 PSEM_SUBSYS_UNLOCK();
1c79356b
A
685 return (EINVAL);
686 }
687
91447636
A
688 if ( (pinfo->psem_flags & PSEM_INDELETE) ) {
689 PSEM_SUBSYS_UNLOCK();
1c79356b
A
690 error = 0;
691 goto bad;
692 }
91447636 693
e5568f75
A
694 AUDIT_ARG(posix_ipc_perm, pinfo->psem_uid, pinfo->psem_gid,
695 pinfo->psem_mode);
696
1c79356b
A
697 pinfo->psem_flags |= PSEM_INDELETE;
698 pinfo->psem_usecount--;
699
700 if (!pinfo->psem_usecount) {
701 psem_delete(pinfo);
91447636 702 FREE(pinfo,M_SHM);
1c79356b
A
703 } else
704 pinfo->psem_flags |= PSEM_REMOVED;
705
706 psem_cache_delete(pcache);
91447636
A
707 PSEM_SUBSYS_UNLOCK();
708 FREE(pcache, M_SHM);
1c79356b
A
709 error = 0;
710bad:
55e303ae 711 FREE_ZONE(pnbuf, MAXPATHLEN, M_NAMEI);
1c79356b
A
712 return (error);
713}
714
1c79356b 715int
91447636 716sem_close(struct proc *p, struct sem_close_args *uap, __unused register_t *retval)
1c79356b 717{
91447636
A
718 int fd = CAST_DOWN(int,uap->sem);
719 struct fileproc *fp;
1c79356b
A
720 int error = 0;
721
e5568f75 722 AUDIT_ARG(fd, fd); /* XXX This seems wrong; uap->sem is a pointer */
91447636
A
723
724 proc_fdlock(p);
725 error = fp_lookup(p,fd, &fp, 1);
726 if (error) {
727 proc_fdunlock(p);
1c79356b 728 return(error);
91447636
A
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);
1c79356b
A
735}
736
1c79356b 737int
91447636 738sem_wait(struct proc *p, struct sem_wait_args *uap, __unused register_t *retval)
1c79356b 739{
91447636
A
740 int fd = CAST_DOWN(int,uap->sem);
741 struct fileproc *fp;
1c79356b
A
742 struct pseminfo * pinfo;
743 struct psemnode * pnode ;
744 kern_return_t kret;
745 int error;
746
91447636
A
747 error = fp_getfpsem(p, fd, &fp, &pnode);
748 if (error)
1c79356b 749 return (error);
91447636
A
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 }
1c79356b
A
760 if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED))
761 != PSEM_ALLOCATED) {
91447636
A
762 PSEM_SUBSYS_UNLOCK();
763 error = EINVAL;
764 goto out;
1c79356b
A
765 }
766
91447636 767 PSEM_SUBSYS_UNLOCK();
1c79356b
A
768 kret = semaphore_wait(pinfo->psem_semobject);
769 switch (kret) {
770 case KERN_INVALID_ADDRESS:
771 case KERN_PROTECTION_FAILURE:
91447636
A
772 error = EACCES;
773 break;
1c79356b
A
774 case KERN_ABORTED:
775 case KERN_OPERATION_TIMED_OUT:
91447636
A
776 error = EINTR;
777 break;
1c79356b 778 case KERN_SUCCESS:
91447636
A
779 error = 0;
780 break;
1c79356b 781 default:
91447636
A
782 error = EINVAL;
783 break;
1c79356b 784 }
91447636
A
785out:
786 fp_drop(p, fd, fp, 0);
787 return(error);
1c79356b 788
91447636 789}
1c79356b
A
790
791int
91447636 792sem_trywait(struct proc *p, struct sem_trywait_args *uap, __unused register_t *retval)
1c79356b 793{
91447636
A
794 int fd = CAST_DOWN(int,uap->sem);
795 struct fileproc *fp;
1c79356b
A
796 struct pseminfo * pinfo;
797 struct psemnode * pnode ;
798 kern_return_t kret;
799 mach_timespec_t wait_time;
800 int error;
801
91447636
A
802 error = fp_getfpsem(p, fd, &fp, &pnode);
803 if (error)
1c79356b 804 return (error);
91447636
A
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 }
1c79356b
A
815 if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED))
816 != PSEM_ALLOCATED) {
91447636
A
817 PSEM_SUBSYS_UNLOCK();
818 error = EINVAL;
819 goto out;
1c79356b
A
820 }
821
91447636 822 PSEM_SUBSYS_UNLOCK();
1c79356b
A
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:
91447636
A
830 error = EINVAL;
831 break;
1c79356b 832 case KERN_ABORTED:
91447636
A
833 error = EINTR;
834 break;
1c79356b 835 case KERN_OPERATION_TIMED_OUT:
91447636
A
836 error = EAGAIN;
837 break;
1c79356b 838 case KERN_SUCCESS:
91447636
A
839 error = 0;
840 break;
1c79356b 841 default:
91447636
A
842 error = EINVAL;
843 break;
1c79356b 844 }
91447636
A
845out:
846 fp_drop(p, fd, fp, 0);
847 return(error);
1c79356b
A
848}
849
1c79356b 850int
91447636 851sem_post(struct proc *p, struct sem_post_args *uap, __unused register_t *retval)
1c79356b 852{
91447636
A
853 int fd = CAST_DOWN(int,uap->sem);
854 struct fileproc *fp;
1c79356b
A
855 struct pseminfo * pinfo;
856 struct psemnode * pnode ;
857 kern_return_t kret;
858 int error;
859
91447636
A
860 error = fp_getfpsem(p, fd, &fp, &pnode);
861 if (error)
1c79356b 862 return (error);
91447636
A
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 }
1c79356b
A
873 if ((pinfo->psem_flags & (PSEM_DEFINED | PSEM_ALLOCATED))
874 != PSEM_ALLOCATED) {
91447636
A
875 PSEM_SUBSYS_UNLOCK();
876 error = EINVAL;
877 goto out;
1c79356b
A
878 }
879
91447636 880 PSEM_SUBSYS_UNLOCK();
1c79356b
A
881 kret = semaphore_signal(pinfo->psem_semobject);
882 switch (kret) {
883 case KERN_INVALID_ADDRESS:
884 case KERN_PROTECTION_FAILURE:
91447636
A
885 error = EINVAL;
886 break;
1c79356b
A
887 case KERN_ABORTED:
888 case KERN_OPERATION_TIMED_OUT:
91447636
A
889 error = EINTR;
890 break;
1c79356b 891 case KERN_SUCCESS:
91447636
A
892 error = 0;
893 break;
1c79356b 894 default:
91447636
A
895 error = EINVAL;
896 break;
1c79356b 897 }
91447636
A
898out:
899 fp_drop(p, fd, fp, 0);
900 return(error);
1c79356b
A
901}
902
1c79356b 903int
91447636 904sem_init(__unused struct proc *p, __unused struct sem_init_args *uap, __unused register_t *retval)
1c79356b
A
905{
906 return(ENOSYS);
907}
908
1c79356b 909int
91447636 910sem_destroy(__unused struct proc *p, __unused struct sem_destroy_args *uap, __unused register_t *retval)
1c79356b
A
911{
912 return(ENOSYS);
913}
914
1c79356b 915int
91447636 916sem_getvalue(__unused struct proc *p, __unused struct sem_getvalue_args *uap, __unused register_t *retval)
1c79356b
A
917{
918 return(ENOSYS);
919}
920
9bccf70c 921static int
91447636
A
922psem_close(struct psemnode *pnode, __unused int flags,
923 __unused kauth_cred_t cred, __unused struct proc *p)
1c79356b
A
924{
925 int error=0;
1c79356b
A
926 register struct pseminfo *pinfo;
927
91447636
A
928 PSEM_SUBSYS_LOCK();
929 if ((pinfo = pnode->pinfo) == PSEMINFO_NULL){
930 PSEM_SUBSYS_UNLOCK();
1c79356b 931 return(EINVAL);
91447636 932 }
1c79356b
A
933
934 if ((pinfo->psem_flags & PSEM_ALLOCATED) != PSEM_ALLOCATED) {
91447636 935 PSEM_SUBSYS_UNLOCK();
1c79356b
A
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) {
91447636
A
946 PSEM_SUBSYS_UNLOCK();
947 /* lock dropped as only semaphore is destroyed here */
1c79356b 948 error = psem_delete(pinfo);
91447636
A
949 FREE(pinfo,M_SHM);
950 } else {
951 PSEM_SUBSYS_UNLOCK();
1c79356b 952 }
91447636
A
953 /* subsystem lock is dropped when we get here */
954 FREE(pnode, M_SHM);
1c79356b
A
955 return (error);
956}
957
9bccf70c 958static int
91447636
A
959psem_closefile(fg, p)
960 struct fileglob *fg;
9bccf70c
A
961 struct proc *p;
962{
91447636 963 int error;
9bccf70c 964
91447636
A
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);
9bccf70c
A
970}
971
91447636 972static int
1c79356b
A
973psem_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 }
1c79356b
A
991}
992
9bccf70c 993static int
91447636
A
994psem_read(__unused struct fileproc *fp, __unused struct uio *uio,
995 __unused kauth_cred_t cred, __unused int flags,
996 __unused struct proc *p)
1c79356b 997{
91447636 998 return(ENOTSUP);
1c79356b 999}
9bccf70c
A
1000
1001static int
91447636
A
1002psem_write(__unused struct fileproc *fp, __unused struct uio *uio,
1003 __unused kauth_cred_t cred, __unused int flags,
1004 __unused struct proc *p)
1c79356b 1005{
91447636 1006 return(ENOTSUP);
1c79356b 1007}
9bccf70c
A
1008
1009static int
91447636
A
1010psem_ioctl(__unused struct fileproc *fp, __unused u_long com,
1011 __unused caddr_t data, __unused struct proc *p)
1c79356b 1012{
91447636 1013 return(ENOTSUP);
1c79356b 1014}
9bccf70c
A
1015
1016static int
91447636
A
1017psem_select(__unused struct fileproc *fp, __unused int which,
1018 __unused void *wql, __unused struct proc *p)
1c79356b 1019{
91447636 1020 return(ENOTSUP);
1c79356b 1021}
55e303ae
A
1022
1023static int
91447636
A
1024psem_kqfilter(__unused struct fileproc *fp, __unused struct knote *kn,
1025 __unused struct proc *p)
55e303ae 1026{
91447636 1027 return (ENOTSUP);
55e303ae
A
1028}
1029