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