Home | History | Annotate | Line # | Download | only in kern
uipc_sem.c revision 1.51
      1 /*	$NetBSD: uipc_sem.c,v 1.51 2018/05/06 00:46:09 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2011 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Mindaugas Rasiukevicius.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * Copyright (c) 2002 Alfred Perlstein <alfred (at) FreeBSD.org>
     34  * All rights reserved.
     35  *
     36  * Redistribution and use in source and binary forms, with or without
     37  * modification, are permitted provided that the following conditions
     38  * are met:
     39  * 1. Redistributions of source code must retain the above copyright
     40  *    notice, this list of conditions and the following disclaimer.
     41  * 2. Redistributions in binary form must reproduce the above copyright
     42  *    notice, this list of conditions and the following disclaimer in the
     43  *    documentation and/or other materials provided with the distribution.
     44  *
     45  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     46  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     48  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     49  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     50  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     51  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     53  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     54  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     55  * SUCH DAMAGE.
     56  */
     57 
     58 /*
     59  * Implementation of POSIX semaphore.
     60  */
     61 
     62 #include <sys/cdefs.h>
     63 __KERNEL_RCSID(0, "$NetBSD: uipc_sem.c,v 1.51 2018/05/06 00:46:09 christos Exp $");
     64 
     65 #include <sys/param.h>
     66 #include <sys/kernel.h>
     67 
     68 #include <sys/atomic.h>
     69 #include <sys/proc.h>
     70 #include <sys/ksem.h>
     71 #include <sys/syscall.h>
     72 #include <sys/stat.h>
     73 #include <sys/kmem.h>
     74 #include <sys/fcntl.h>
     75 #include <sys/file.h>
     76 #include <sys/filedesc.h>
     77 #include <sys/kauth.h>
     78 #include <sys/module.h>
     79 #include <sys/mount.h>
     80 #include <sys/semaphore.h>
     81 #include <sys/syscall.h>
     82 #include <sys/syscallargs.h>
     83 #include <sys/syscallvar.h>
     84 #include <sys/sysctl.h>
     85 
     86 MODULE(MODULE_CLASS_MISC, ksem, NULL);
     87 
     88 #define	SEM_MAX_NAMELEN		NAME_MAX
     89 
     90 #define	SEM_NSEMS_MAX		256
     91 #define	KS_UNLINKED		0x01
     92 
     93 static kmutex_t		ksem_lock	__cacheline_aligned;
     94 static LIST_HEAD(,ksem)	ksem_head	__cacheline_aligned;
     95 static u_int		nsems_total	__cacheline_aligned;
     96 static u_int		nsems		__cacheline_aligned;
     97 
     98 static kauth_listener_t	ksem_listener;
     99 
    100 static int		ksem_sysinit(void);
    101 static int		ksem_sysfini(bool);
    102 static int		ksem_modcmd(modcmd_t, void *);
    103 static int		ksem_close_fop(file_t *);
    104 static int		ksem_stat_fop(file_t *, struct stat *);
    105 static int		ksem_read_fop(file_t *, off_t *, struct uio *,
    106     kauth_cred_t, int);
    107 
    108 static const struct fileops semops = {
    109 	.fo_name = "sem",
    110 	.fo_read = ksem_read_fop,
    111 	.fo_write = fbadop_write,
    112 	.fo_ioctl = fbadop_ioctl,
    113 	.fo_fcntl = fnullop_fcntl,
    114 	.fo_poll = fnullop_poll,
    115 	.fo_stat = ksem_stat_fop,
    116 	.fo_close = ksem_close_fop,
    117 	.fo_kqfilter = fnullop_kqfilter,
    118 	.fo_restart = fnullop_restart,
    119 };
    120 
    121 static const struct syscall_package ksem_syscalls[] = {
    122 	{ SYS__ksem_init, 0, (sy_call_t *)sys__ksem_init },
    123 	{ SYS__ksem_open, 0, (sy_call_t *)sys__ksem_open },
    124 	{ SYS__ksem_unlink, 0, (sy_call_t *)sys__ksem_unlink },
    125 	{ SYS__ksem_close, 0, (sy_call_t *)sys__ksem_close },
    126 	{ SYS__ksem_post, 0, (sy_call_t *)sys__ksem_post },
    127 	{ SYS__ksem_wait, 0, (sy_call_t *)sys__ksem_wait },
    128 	{ SYS__ksem_trywait, 0, (sy_call_t *)sys__ksem_trywait },
    129 	{ SYS__ksem_getvalue, 0, (sy_call_t *)sys__ksem_getvalue },
    130 	{ SYS__ksem_destroy, 0, (sy_call_t *)sys__ksem_destroy },
    131 	{ SYS__ksem_timedwait, 0, (sy_call_t *)sys__ksem_timedwait },
    132 	{ 0, 0, NULL },
    133 };
    134 
    135 struct sysctllog *ksem_clog;
    136 int ksem_max;
    137 
    138 static int
    139 name_copyin(const char *uname, char **name)
    140 {
    141 	*name = kmem_alloc(SEM_MAX_NAMELEN, KM_SLEEP);
    142 
    143 	int error = copyinstr(uname, *name, SEM_MAX_NAMELEN, NULL);
    144 	if (error)
    145 		kmem_free(*name, SEM_MAX_NAMELEN);
    146 
    147 	return error;
    148 }
    149 
    150 static void
    151 name_destroy(char **name)
    152 {
    153 	if (!*name)
    154 		return;
    155 
    156 	kmem_free(*name, SEM_MAX_NAMELEN);
    157 	*name = NULL;
    158 }
    159 
    160 static int
    161 ksem_listener_cb(kauth_cred_t cred, kauth_action_t action, void *cookie,
    162     void *arg0, void *arg1, void *arg2, void *arg3)
    163 {
    164 	ksem_t *ks;
    165 	mode_t mode;
    166 
    167 	if (action != KAUTH_SYSTEM_SEMAPHORE)
    168 		return KAUTH_RESULT_DEFER;
    169 
    170 	ks = arg1;
    171 	mode = ks->ks_mode;
    172 
    173 	if ((kauth_cred_geteuid(cred) == ks->ks_uid && (mode & S_IWUSR) != 0) ||
    174 	    (kauth_cred_getegid(cred) == ks->ks_gid && (mode & S_IWGRP) != 0) ||
    175 	    (mode & S_IWOTH) != 0)
    176 		return KAUTH_RESULT_ALLOW;
    177 
    178 	return KAUTH_RESULT_DEFER;
    179 }
    180 
    181 static int
    182 ksem_sysinit(void)
    183 {
    184 	int error;
    185 	const struct sysctlnode *rnode;
    186 
    187 	mutex_init(&ksem_lock, MUTEX_DEFAULT, IPL_NONE);
    188 	LIST_INIT(&ksem_head);
    189 	nsems_total = 0;
    190 	nsems = 0;
    191 
    192 	error = syscall_establish(NULL, ksem_syscalls);
    193 	if (error) {
    194 		(void)ksem_sysfini(false);
    195 	}
    196 
    197 	ksem_listener = kauth_listen_scope(KAUTH_SCOPE_SYSTEM,
    198 	    ksem_listener_cb, NULL);
    199 
    200 	/* Define module-specific sysctl tree */
    201 
    202 	ksem_max = KSEM_MAX;
    203 	ksem_clog = NULL;
    204 
    205 	sysctl_createv(&ksem_clog, 0, NULL, &rnode,
    206 			CTLFLAG_PERMANENT,
    207 			CTLTYPE_NODE, "posix",
    208 			SYSCTL_DESCR("POSIX options"),
    209 			NULL, 0, NULL, 0,
    210 			CTL_KERN, CTL_CREATE, CTL_EOL);
    211 	sysctl_createv(&ksem_clog, 0, &rnode, NULL,
    212 			CTLFLAG_PERMANENT | CTLFLAG_READWRITE,
    213 			CTLTYPE_INT, "semmax",
    214 			SYSCTL_DESCR("Maximal number of semaphores"),
    215 			NULL, 0, &ksem_max, 0,
    216 			CTL_CREATE, CTL_EOL);
    217 	sysctl_createv(&ksem_clog, 0, &rnode, NULL,
    218 			CTLFLAG_PERMANENT | CTLFLAG_READONLY,
    219 			CTLTYPE_INT, "semcnt",
    220 			SYSCTL_DESCR("Current number of semaphores"),
    221 			NULL, 0, &nsems, 0,
    222 			CTL_CREATE, CTL_EOL);
    223 
    224 	return error;
    225 }
    226 
    227 static int
    228 ksem_sysfini(bool interface)
    229 {
    230 	int error;
    231 
    232 	if (interface) {
    233 		error = syscall_disestablish(NULL, ksem_syscalls);
    234 		if (error != 0) {
    235 			return error;
    236 		}
    237 		/*
    238 		 * Make sure that no semaphores are in use.  Note: semops
    239 		 * must be unused at this point.
    240 		 */
    241 		if (nsems_total) {
    242 			error = syscall_establish(NULL, ksem_syscalls);
    243 			KASSERT(error == 0);
    244 			return EBUSY;
    245 		}
    246 	}
    247 	kauth_unlisten_scope(ksem_listener);
    248 	mutex_destroy(&ksem_lock);
    249 	sysctl_teardown(&ksem_clog);
    250 	return 0;
    251 }
    252 
    253 static int
    254 ksem_modcmd(modcmd_t cmd, void *arg)
    255 {
    256 
    257 	switch (cmd) {
    258 	case MODULE_CMD_INIT:
    259 		return ksem_sysinit();
    260 
    261 	case MODULE_CMD_FINI:
    262 		return ksem_sysfini(true);
    263 
    264 	default:
    265 		return ENOTTY;
    266 	}
    267 }
    268 
    269 static ksem_t *
    270 ksem_lookup(const char *name)
    271 {
    272 	ksem_t *ks;
    273 
    274 	KASSERT(mutex_owned(&ksem_lock));
    275 
    276 	LIST_FOREACH(ks, &ksem_head, ks_entry) {
    277 		if (strcmp(ks->ks_name, name) == 0) {
    278 			mutex_enter(&ks->ks_lock);
    279 			return ks;
    280 		}
    281 	}
    282 	return NULL;
    283 }
    284 
    285 static int
    286 ksem_perm(lwp_t *l, ksem_t *ks)
    287 {
    288 	kauth_cred_t uc = l->l_cred;
    289 
    290 	KASSERT(mutex_owned(&ks->ks_lock));
    291 
    292 	if (kauth_authorize_system(uc, KAUTH_SYSTEM_SEMAPHORE, 0, ks, NULL, NULL) != 0)
    293 		return EACCES;
    294 
    295 	return 0;
    296 }
    297 
    298 /*
    299  * ksem_get: get the semaphore from the descriptor.
    300  *
    301  * => locks the semaphore, if found.
    302  * => holds a reference on the file descriptor.
    303  */
    304 static int
    305 ksem_get(int fd, ksem_t **ksret)
    306 {
    307 	ksem_t *ks;
    308 	file_t *fp;
    309 
    310 	fp = fd_getfile(fd);
    311 	if (__predict_false(fp == NULL))
    312 		return EINVAL;
    313 	if (__predict_false(fp->f_type != DTYPE_SEM)) {
    314 		fd_putfile(fd);
    315 		return EINVAL;
    316 	}
    317 	ks = fp->f_ksem;
    318 	mutex_enter(&ks->ks_lock);
    319 
    320 	*ksret = ks;
    321 	return 0;
    322 }
    323 
    324 /*
    325  * ksem_create: allocate and setup a new semaphore structure.
    326  */
    327 static int
    328 ksem_create(lwp_t *l, const char *name, ksem_t **ksret, mode_t mode, u_int val)
    329 {
    330 	ksem_t *ks;
    331 	kauth_cred_t uc;
    332 	char *kname;
    333 	size_t len;
    334 
    335 	/* Pre-check for the limit. */
    336 	if (nsems >= ksem_max) {
    337 		return ENFILE;
    338 	}
    339 
    340 	if (val > SEM_VALUE_MAX) {
    341 		return EINVAL;
    342 	}
    343 
    344 	if (name != NULL) {
    345 		len = strlen(name);
    346 		if (len > SEM_MAX_NAMELEN) {
    347 			return ENAMETOOLONG;
    348 		}
    349 		/* Name must start with a '/' but not contain one. */
    350 		if (*name != '/' || len < 2 || strchr(name + 1, '/') != NULL) {
    351 			return EINVAL;
    352 		}
    353 		kname = kmem_alloc(++len, KM_SLEEP);
    354 		strlcpy(kname, name, len);
    355 	} else {
    356 		kname = NULL;
    357 		len = 0;
    358 	}
    359 
    360 	if (atomic_inc_uint_nv(&l->l_proc->p_nsems) > SEM_NSEMS_MAX) {
    361 		atomic_dec_uint(&l->l_proc->p_nsems);
    362 		if (kname != NULL)
    363 			kmem_free(kname, len);
    364 		return -1;
    365 	}
    366 
    367 	ks = kmem_zalloc(sizeof(ksem_t), KM_SLEEP);
    368 	mutex_init(&ks->ks_lock, MUTEX_DEFAULT, IPL_NONE);
    369 	cv_init(&ks->ks_cv, "psem");
    370 	ks->ks_name = kname;
    371 	ks->ks_namelen = len;
    372 	ks->ks_mode = mode;
    373 	ks->ks_value = val;
    374 	ks->ks_ref = 1;
    375 
    376 	uc = l->l_cred;
    377 	ks->ks_uid = kauth_cred_geteuid(uc);
    378 	ks->ks_gid = kauth_cred_getegid(uc);
    379 
    380 	atomic_inc_uint(&nsems_total);
    381 	*ksret = ks;
    382 	return 0;
    383 }
    384 
    385 static void
    386 ksem_free(ksem_t *ks)
    387 {
    388 
    389 	KASSERT(!cv_has_waiters(&ks->ks_cv));
    390 
    391 	if (ks->ks_name) {
    392 		KASSERT(ks->ks_namelen > 0);
    393 		kmem_free(ks->ks_name, ks->ks_namelen);
    394 	}
    395 	mutex_destroy(&ks->ks_lock);
    396 	cv_destroy(&ks->ks_cv);
    397 	kmem_free(ks, sizeof(ksem_t));
    398 
    399 	atomic_dec_uint(&nsems_total);
    400  	atomic_dec_uint(&curproc->p_nsems);
    401 }
    402 
    403 int
    404 sys__ksem_init(struct lwp *l, const struct sys__ksem_init_args *uap,
    405     register_t *retval)
    406 {
    407 	/* {
    408 		unsigned int value;
    409 		intptr_t *idp;
    410 	} */
    411 
    412 	return do_ksem_init(l, SCARG(uap, value), SCARG(uap, idp), copyout);
    413 }
    414 
    415 int
    416 do_ksem_init(lwp_t *l, u_int val, intptr_t *idp, copyout_t docopyout)
    417 {
    418 	proc_t *p = l->l_proc;
    419 	ksem_t *ks;
    420 	file_t *fp;
    421 	intptr_t id;
    422 	int fd, error;
    423 
    424 	error = fd_allocfile(&fp, &fd);
    425 	if (error) {
    426 		return error;
    427 	}
    428 	fp->f_type = DTYPE_SEM;
    429 	fp->f_flag = FREAD | FWRITE;
    430 	fp->f_ops = &semops;
    431 
    432 	id = (intptr_t)fd;
    433 	error = (*docopyout)(&id, idp, sizeof(*idp));
    434 	if (error) {
    435 		fd_abort(p, fp, fd);
    436 		return error;
    437 	}
    438 
    439 	/* Note the mode does not matter for anonymous semaphores. */
    440 	error = ksem_create(l, NULL, &ks, 0, val);
    441 	if (error) {
    442 		fd_abort(p, fp, fd);
    443 		return error;
    444 	}
    445 	fp->f_ksem = ks;
    446 	fd_affix(p, fp, fd);
    447 	return error;
    448 }
    449 
    450 int
    451 sys__ksem_open(struct lwp *l, const struct sys__ksem_open_args *uap,
    452     register_t *retval)
    453 {
    454 	/* {
    455 		const char *name;
    456 		int oflag;
    457 		mode_t mode;
    458 		unsigned int value;
    459 		intptr_t *idp;
    460 	} */
    461 
    462 	return do_ksem_open(l, SCARG(uap, name), SCARG(uap, oflag),
    463 	    SCARG(uap, mode), SCARG(uap, value), SCARG(uap, idp), copyout);
    464 }
    465 
    466 int
    467 do_ksem_open(struct lwp *l, const char *semname, int oflag, mode_t mode,
    468      unsigned int value, intptr_t *idp, copyout_t docopyout)
    469 {
    470 	char *name;
    471 	proc_t *p = l->l_proc;
    472 	ksem_t *ksnew = NULL, *ks;
    473 	file_t *fp;
    474 	intptr_t id;
    475 	int fd, error;
    476 
    477 	error = name_copyin(semname, &name);
    478 	if (error) {
    479 		return error;
    480 	}
    481 	error = fd_allocfile(&fp, &fd);
    482 	if (error) {
    483 		name_destroy(&name);
    484 		return error;
    485 	}
    486 	fp->f_type = DTYPE_SEM;
    487 	fp->f_flag = FREAD | FWRITE;
    488 	fp->f_ops = &semops;
    489 
    490 	/*
    491 	 * The ID (file descriptor number) can be stored early.
    492 	 * Note that zero is a special value for libpthread.
    493 	 */
    494 	id = (intptr_t)fd;
    495 	error = (*docopyout)(&id, idp, sizeof(*idp));
    496 	if (error) {
    497 		goto err;
    498 	}
    499 
    500 	if (oflag & O_CREAT) {
    501 		/* Create a new semaphore. */
    502 		error = ksem_create(l, name, &ksnew, mode, value);
    503 		if (error) {
    504 			goto err;
    505 		}
    506 		KASSERT(ksnew != NULL);
    507 	}
    508 
    509 	/* Lookup for a semaphore with such name. */
    510 	mutex_enter(&ksem_lock);
    511 	ks = ksem_lookup(name);
    512 	name_destroy(&name);
    513 	if (ks) {
    514 		KASSERT(mutex_owned(&ks->ks_lock));
    515 		mutex_exit(&ksem_lock);
    516 
    517 		/* Check for exclusive create. */
    518 		if (oflag & O_EXCL) {
    519 			mutex_exit(&ks->ks_lock);
    520 			error = EEXIST;
    521 			goto err;
    522 		}
    523 		/*
    524 		 * Verify permissions.  If we can access it,
    525 		 * add the reference of this thread.
    526 		 */
    527 		error = ksem_perm(l, ks);
    528 		if (error == 0) {
    529 			ks->ks_ref++;
    530 		}
    531 		mutex_exit(&ks->ks_lock);
    532 		if (error) {
    533 			goto err;
    534 		}
    535 	} else {
    536 		/* Fail if not found and not creating. */
    537 		if ((oflag & O_CREAT) == 0) {
    538 			mutex_exit(&ksem_lock);
    539 			KASSERT(ksnew == NULL);
    540 			error = ENOENT;
    541 			goto err;
    542 		}
    543 
    544 		/* Check for the limit locked. */
    545 		if (nsems >= ksem_max) {
    546 			mutex_exit(&ksem_lock);
    547 			error = ENFILE;
    548 			goto err;
    549 		}
    550 
    551 		/*
    552 		 * Finally, insert semaphore into the list.
    553 		 * Note: it already has the initial reference.
    554 		 */
    555 		ks = ksnew;
    556 		LIST_INSERT_HEAD(&ksem_head, ks, ks_entry);
    557 		nsems++;
    558 		mutex_exit(&ksem_lock);
    559 
    560 		ksnew = NULL;
    561 	}
    562 	KASSERT(ks != NULL);
    563 	fp->f_ksem = ks;
    564 	fd_affix(p, fp, fd);
    565 err:
    566 	name_destroy(&name);
    567 	if (error) {
    568 		fd_abort(p, fp, fd);
    569 	}
    570 	if (ksnew) {
    571 		ksem_free(ksnew);
    572 	}
    573 	return error;
    574 }
    575 
    576 int
    577 sys__ksem_close(struct lwp *l, const struct sys__ksem_close_args *uap,
    578     register_t *retval)
    579 {
    580 	/* {
    581 		intptr_t id;
    582 	} */
    583 	int fd = (int)SCARG(uap, id);
    584 
    585 	if (fd_getfile(fd) == NULL) {
    586 		return EBADF;
    587 	}
    588 	return fd_close(fd);
    589 }
    590 
    591 static int
    592 ksem_read_fop(file_t *fp, off_t *offset, struct uio *uio, kauth_cred_t cred,
    593     int flags)
    594 {
    595 	size_t len;
    596 	char *name;
    597 	ksem_t *ks = fp->f_ksem;
    598 
    599 	mutex_enter(&ks->ks_lock);
    600 	len = ks->ks_namelen;
    601 	name = ks->ks_name;
    602 	mutex_exit(&ks->ks_lock);
    603 	if (name == NULL || len == 0)
    604 		return 0;
    605 	return uiomove(name, len, uio);
    606 }
    607 
    608 static int
    609 ksem_stat_fop(file_t *fp, struct stat *ub)
    610 {
    611 	ksem_t *ks = fp->f_ksem;
    612 
    613 	mutex_enter(&ks->ks_lock);
    614 
    615 	memset(ub, 0, sizeof(*ub));
    616 
    617 	ub->st_mode = ks->ks_mode | ((ks->ks_name && ks->ks_namelen)
    618 	    ? _S_IFLNK : _S_IFREG);
    619 	ub->st_uid = ks->ks_uid;
    620 	ub->st_gid = ks->ks_gid;
    621 	ub->st_size = ks->ks_value;
    622 	ub->st_blocks = (ub->st_size) ? 1 : 0;
    623 	ub->st_nlink = ks->ks_ref;
    624 	ub->st_blksize = 4096;
    625 
    626 	nanotime(&ub->st_atimespec);
    627 	ub->st_mtimespec = ub->st_ctimespec = ub->st_birthtimespec =
    628 	    ub->st_atimespec;
    629 
    630 	/*
    631 	 * Left as 0: st_dev, st_ino, st_rdev, st_flags, st_gen.
    632 	 * XXX (st_dev, st_ino) should be unique.
    633 	 */
    634 	mutex_exit(&ks->ks_lock);
    635 	return 0;
    636 }
    637 
    638 static int
    639 ksem_close_fop(file_t *fp)
    640 {
    641 	ksem_t *ks = fp->f_ksem;
    642 	bool destroy = false;
    643 
    644 	mutex_enter(&ks->ks_lock);
    645 	KASSERT(ks->ks_ref > 0);
    646 	if (--ks->ks_ref == 0) {
    647 		/*
    648 		 * Destroy if the last reference and semaphore is unnamed,
    649 		 * or unlinked (for named semaphore).
    650 		 */
    651 		destroy = (ks->ks_flags & KS_UNLINKED) || (ks->ks_name == NULL);
    652 	}
    653 	mutex_exit(&ks->ks_lock);
    654 
    655 	if (destroy) {
    656 		ksem_free(ks);
    657 	}
    658 	return 0;
    659 }
    660 
    661 int
    662 sys__ksem_unlink(struct lwp *l, const struct sys__ksem_unlink_args *uap,
    663     register_t *retval)
    664 {
    665 	/* {
    666 		const char *name;
    667 	} */
    668 	char *name;
    669 	ksem_t *ks;
    670 	u_int refcnt;
    671 	int error;
    672 
    673 	error = name_copyin(SCARG(uap, name), &name);
    674 	if (error)
    675 		return error;
    676 
    677 	mutex_enter(&ksem_lock);
    678 	ks = ksem_lookup(name);
    679 	name_destroy(&name);
    680 	if (ks == NULL) {
    681 		mutex_exit(&ksem_lock);
    682 		return ENOENT;
    683 	}
    684 	KASSERT(mutex_owned(&ks->ks_lock));
    685 
    686 	/* Verify permissions. */
    687 	error = ksem_perm(l, ks);
    688 	if (error) {
    689 		mutex_exit(&ks->ks_lock);
    690 		mutex_exit(&ksem_lock);
    691 		return error;
    692 	}
    693 
    694 	/* Remove from the global list. */
    695 	LIST_REMOVE(ks, ks_entry);
    696 	nsems--;
    697 	mutex_exit(&ksem_lock);
    698 
    699 	refcnt = ks->ks_ref;
    700 	if (refcnt) {
    701 		/* Mark as unlinked, if there are references. */
    702 		ks->ks_flags |= KS_UNLINKED;
    703 	}
    704 	mutex_exit(&ks->ks_lock);
    705 
    706 	if (refcnt == 0) {
    707 		ksem_free(ks);
    708 	}
    709 	return 0;
    710 }
    711 
    712 int
    713 sys__ksem_post(struct lwp *l, const struct sys__ksem_post_args *uap,
    714     register_t *retval)
    715 {
    716 	/* {
    717 		intptr_t id;
    718 	} */
    719 	int fd = (int)SCARG(uap, id), error;
    720 	ksem_t *ks;
    721 
    722 	error = ksem_get(fd, &ks);
    723 	if (error) {
    724 		return error;
    725 	}
    726 	KASSERT(mutex_owned(&ks->ks_lock));
    727 	if (ks->ks_value == SEM_VALUE_MAX) {
    728 		error = EOVERFLOW;
    729 		goto out;
    730 	}
    731 	ks->ks_value++;
    732 	if (ks->ks_waiters) {
    733 		cv_broadcast(&ks->ks_cv);
    734 	}
    735 out:
    736 	mutex_exit(&ks->ks_lock);
    737 	fd_putfile(fd);
    738 	return error;
    739 }
    740 
    741 int
    742 do_ksem_wait(lwp_t *l, intptr_t id, bool try_p, struct timespec *abstime)
    743 {
    744 	int fd = (int)id, error, timeo;
    745 	ksem_t *ks;
    746 
    747 	error = ksem_get(fd, &ks);
    748 	if (error) {
    749 		return error;
    750 	}
    751 	KASSERT(mutex_owned(&ks->ks_lock));
    752 	while (ks->ks_value == 0) {
    753 		ks->ks_waiters++;
    754 		if (!try_p && abstime != NULL) {
    755 			error = ts2timo(CLOCK_REALTIME, TIMER_ABSTIME, abstime,
    756 			    &timeo, NULL);
    757 			if (error != 0)
    758 				goto out;
    759 		} else {
    760 			timeo = 0;
    761 		}
    762 		error = try_p ? EAGAIN : cv_timedwait_sig(&ks->ks_cv,
    763 		    &ks->ks_lock, timeo);
    764 		ks->ks_waiters--;
    765 		if (error)
    766 			goto out;
    767 	}
    768 	ks->ks_value--;
    769 out:
    770 	mutex_exit(&ks->ks_lock);
    771 	fd_putfile(fd);
    772 	return error;
    773 }
    774 
    775 int
    776 sys__ksem_wait(struct lwp *l, const struct sys__ksem_wait_args *uap,
    777     register_t *retval)
    778 {
    779 	/* {
    780 		intptr_t id;
    781 	} */
    782 
    783 	return do_ksem_wait(l, SCARG(uap, id), false, NULL);
    784 }
    785 
    786 int
    787 sys__ksem_timedwait(struct lwp *l, const struct sys__ksem_timedwait_args *uap,
    788     register_t *retval)
    789 {
    790 	/* {
    791 		intptr_t id;
    792 		const struct timespec *abstime;
    793 	} */
    794 	struct timespec ts;
    795 	int error;
    796 
    797 	error = copyin(SCARG(uap, abstime), &ts, sizeof(ts));
    798 	if (error != 0)
    799 		return error;
    800 
    801 	if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= 1000000000)
    802 		return EINVAL;
    803 
    804 	error = do_ksem_wait(l, SCARG(uap, id), false, &ts);
    805 	if (error == EWOULDBLOCK)
    806 		error = ETIMEDOUT;
    807 	return error;
    808 }
    809 
    810 int
    811 sys__ksem_trywait(struct lwp *l, const struct sys__ksem_trywait_args *uap,
    812     register_t *retval)
    813 {
    814 	/* {
    815 		intptr_t id;
    816 	} */
    817 
    818 	return do_ksem_wait(l, SCARG(uap, id), true, NULL);
    819 }
    820 
    821 int
    822 sys__ksem_getvalue(struct lwp *l, const struct sys__ksem_getvalue_args *uap,
    823     register_t *retval)
    824 {
    825 	/* {
    826 		intptr_t id;
    827 		unsigned int *value;
    828 	} */
    829 	int fd = (int)SCARG(uap, id), error;
    830 	ksem_t *ks;
    831 	unsigned int val;
    832 
    833 	error = ksem_get(fd, &ks);
    834 	if (error) {
    835 		return error;
    836 	}
    837 	KASSERT(mutex_owned(&ks->ks_lock));
    838 	val = ks->ks_value;
    839 	mutex_exit(&ks->ks_lock);
    840 	fd_putfile(fd);
    841 
    842 	return copyout(&val, SCARG(uap, value), sizeof(val));
    843 }
    844 
    845 int
    846 sys__ksem_destroy(struct lwp *l, const struct sys__ksem_destroy_args *uap,
    847     register_t *retval)
    848 {
    849 	/* {
    850 		intptr_t id;
    851 	} */
    852 	int fd = (int)SCARG(uap, id), error;
    853 	ksem_t *ks;
    854 
    855 	error = ksem_get(fd, &ks);
    856 	if (error) {
    857 		return error;
    858 	}
    859 	KASSERT(mutex_owned(&ks->ks_lock));
    860 
    861 	/* Operation is only for unnamed semaphores. */
    862 	if (ks->ks_name != NULL) {
    863 		error = EINVAL;
    864 		goto out;
    865 	}
    866 	/* Cannot destroy if there are waiters. */
    867 	if (ks->ks_waiters) {
    868 		error = EBUSY;
    869 		goto out;
    870 	}
    871 out:
    872 	mutex_exit(&ks->ks_lock);
    873 	if (error) {
    874 		fd_putfile(fd);
    875 		return error;
    876 	}
    877 	return fd_close(fd);
    878 }
    879