--- /dev/null
+/*
+ * Copyright (c) 2012 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/filedesc.h>
+#include <sys/kernel.h>
+#include <sys/file_internal.h>
+#include <sys/guarded.h>
+#include <kern/kalloc.h>
+#include <sys/sysproto.h>
+#include <sys/vnode.h>
+#include <vfs/vfs_support.h>
+#include <security/audit/audit.h>
+
+/*
+ * Experimental guarded file descriptor support.
+ */
+
+kern_return_t task_exception_notify(exception_type_t exception,
+ mach_exception_data_type_t code, mach_exception_data_type_t subcode);
+
+/*
+ * Most fd's have an underlying fileproc struct; but some may be
+ * guarded_fileproc structs which implement guarded fds. The latter
+ * struct (below) embeds the former.
+ *
+ * The two types should be distinguished by the "type" portion of f_flags.
+ * There's also a magic number to help catch misuse and bugs.
+ *
+ * This is a bit unpleasant, but results from the desire to allow
+ * alternate file behaviours for a few file descriptors without
+ * growing the fileproc data structure.
+ */
+
+struct guarded_fileproc {
+ struct fileproc gf_fileproc;
+ u_int gf_magic;
+ u_int gf_attrs;
+ thread_t gf_thread;
+ guardid_t gf_guard;
+ int gf_exc_fd;
+ u_int gf_exc_code;
+};
+
+const size_t sizeof_guarded_fileproc = sizeof (struct guarded_fileproc);
+
+#define FP_TO_GFP(fp) ((struct guarded_fileproc *)(fp))
+#define GFP_TO_FP(gfp) (&(gfp)->gf_fileproc)
+
+#define GUARDED_FILEPROC_MAGIC 0x29083
+
+struct gfp_crarg {
+ guardid_t gca_guard;
+ u_int gca_attrs;
+};
+
+static struct fileproc *
+guarded_fileproc_alloc_init(void *crarg)
+{
+ struct gfp_crarg *aarg = crarg;
+ struct guarded_fileproc *gfp;
+
+ if ((gfp = kalloc(sizeof (*gfp))) == NULL)
+ return (NULL);
+
+ bzero(gfp, sizeof (*gfp));
+ gfp->gf_fileproc.f_flags = FTYPE_GUARDED;
+ gfp->gf_magic = GUARDED_FILEPROC_MAGIC;
+ gfp->gf_guard = aarg->gca_guard;
+ gfp->gf_attrs = aarg->gca_attrs;
+
+ return (GFP_TO_FP(gfp));
+}
+
+void
+guarded_fileproc_free(struct fileproc *fp)
+{
+ struct guarded_fileproc *gfp = FP_TO_GFP(fp);
+
+ if (FILEPROC_TYPE(fp) != FTYPE_GUARDED ||
+ GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
+ panic("%s: corrupt fp %p flags %x", __func__, fp, fp->f_flags);
+
+ kfree(gfp, sizeof (*gfp));
+}
+
+static int
+fp_lookup_guarded(proc_t p, int fd, guardid_t guard,
+ struct guarded_fileproc **gfpp)
+{
+ struct fileproc *fp;
+ int error;
+
+ if ((error = fp_lookup(p, fd, &fp, 1)) != 0)
+ return (error);
+ if (FILEPROC_TYPE(fp) != FTYPE_GUARDED) {
+ (void) fp_drop(p, fd, fp, 1);
+ return (EINVAL);
+ }
+ struct guarded_fileproc *gfp = FP_TO_GFP(fp);
+
+ if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
+ panic("%s: corrupt fp %p", __func__, fp);
+
+ if (guard != gfp->gf_guard) {
+ (void) fp_drop(p, fd, fp, 1);
+ return (EPERM); /* *not* a mismatch exception */
+ }
+ if (gfpp)
+ *gfpp = gfp;
+ return (0);
+}
+
+/*
+ * Expected use pattern:
+ *
+ * if (FP_ISGUARDED(fp, GUARD_CLOSE)) {
+ * error = fp_guard_exception(p, fd, fp, kGUARD_EXC_CLOSE);
+ * proc_fdunlock(p);
+ * return (error);
+ * }
+ */
+
+int
+fp_isguarded(struct fileproc *fp, u_int attrs)
+{
+ if (FILEPROC_TYPE(fp) == FTYPE_GUARDED) {
+ struct guarded_fileproc *gfp = FP_TO_GFP(fp);
+
+ if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
+ panic("%s: corrupt gfp %p flags %x",
+ __func__, gfp, fp->f_flags);
+ return ((attrs & gfp->gf_attrs) ? 1 : 0);
+ }
+ return (0);
+}
+
+extern char *proc_name_address(void *p);
+
+int
+fp_guard_exception(proc_t p, int fd, struct fileproc *fp, u_int code)
+{
+ if (FILEPROC_TYPE(fp) != FTYPE_GUARDED)
+ panic("%s corrupt fp %p flags %x", __func__, fp, fp->f_flags);
+
+ struct guarded_fileproc *gfp = FP_TO_GFP(fp);
+
+ /* all gfd fields protected via proc_fdlock() */
+ proc_fdlock_assert(p, LCK_MTX_ASSERT_OWNED);
+
+ if (NULL == gfp->gf_thread) {
+ thread_t t = current_thread();
+ gfp->gf_thread = t;
+ gfp->gf_exc_fd = fd;
+ gfp->gf_exc_code = code;
+
+ /*
+ * This thread was the first to attempt the
+ * operation that violated the guard on this fd;
+ * generate an exception.
+ */
+ printf("%s: guarded fd exception: "
+ "fd %d code 0x%x guard 0x%llx\n",
+ proc_name_address(p), gfp->gf_exc_fd,
+ gfp->gf_exc_code, gfp->gf_guard);
+
+ thread_guard_violation(t, GUARD_TYPE_FD);
+ } else {
+ /*
+ * We already recorded a violation on this fd for a
+ * different thread, so posting an exception is
+ * already in progress. We could pause for a bit
+ * and check again, or we could panic (though that seems
+ * heavy handed), or we could just press on with the
+ * error return alone. For now, resort to printf.
+ */
+ printf("%s: guarded fd exception+: "
+ "fd %d code 0x%x guard 0x%llx\n",
+ proc_name_address(p), gfp->gf_exc_fd,
+ gfp->gf_exc_code, gfp->gf_guard);
+ }
+
+ return (EPERM);
+}
+
+/*
+ * (Invoked before returning to userland from the syscall handler.)
+ */
+void
+fd_guard_ast(thread_t t)
+{
+ proc_t p = current_proc();
+ struct filedesc *fdp = p->p_fd;
+ int i;
+
+ proc_fdlock(p);
+ for (i = fdp->fd_lastfile; i >= 0; i--) {
+ struct fileproc *fp = fdp->fd_ofiles[i];
+
+ if (fp == NULL ||
+ FILEPROC_TYPE(fp) != FTYPE_GUARDED)
+ continue;
+
+ struct guarded_fileproc *gfp = FP_TO_GFP(fp);
+
+ if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
+ panic("%s: corrupt gfp %p flags %x",
+ __func__, gfp, fp->f_flags);
+
+ if (gfp->gf_thread == t) {
+ mach_exception_data_type_t code, subcode;
+
+ gfp->gf_thread = NULL;
+
+ /*
+ * EXC_GUARD exception code namespace.
+ *
+ * code:
+ * +-------------------------------------------------+
+ * | [63:61] guard type | [60:0] guard-specific data |
+ * +-------------------------------------------------+
+ *
+ * subcode:
+ * +-------------------------------------------------+
+ * | [63:0] guard-specific data |
+ * +-------------------------------------------------+
+ *
+ * At the moment, we have just one guard type: file
+ * descriptor guards.
+ *
+ * File descriptor guards use the exception codes like
+ * so:
+ *
+ * code:
+ * +--------------------------------------------------+
+ * |[63:61] GUARD_TYPE_FD | [60:32] flavor | [31:0] fd|
+ * +--------------------------------------------------+
+ *
+ * subcode:
+ * +--------------------------------------------------+
+ * | [63:0] guard value |
+ * +--------------------------------------------------+
+ */
+ code = (((uint64_t)GUARD_TYPE_FD) << 61) |
+ (((uint64_t)gfp->gf_exc_code) << 32) |
+ ((uint64_t)gfp->gf_exc_fd);
+ subcode = gfp->gf_guard;
+ proc_fdunlock(p);
+
+ (void) task_exception_notify(EXC_GUARD, code, subcode);
+ psignal(p, SIGKILL);
+
+ return;
+ }
+ }
+ proc_fdunlock(p);
+}
+
+/*
+ * Experimental guarded file descriptor SPIs
+ */
+
+/*
+ * int guarded_open_np(const char *pathname, int flags,
+ * const guardid_t *guard, u_int guardflags, ...);
+ *
+ * In this initial implementation, GUARD_DUP must be specified.
+ * GUARD_CLOSE, GUARD_SOCKET_IPC and GUARD_FILEPORT are optional.
+ *
+ * If GUARD_DUP wasn't specified, then we'd have to do the (extra) work
+ * to allow dup-ing a descriptor to inherit the guard onto the new
+ * descriptor. (Perhaps GUARD_DUP behaviours should just always be true
+ * for a guarded fd? Or, more sanely, all the dup operations should
+ * just always propagate the guard?)
+ *
+ * Guarded descriptors are always close-on-exec, and GUARD_CLOSE
+ * requires close-on-fork; O_CLOEXEC must be set in flags.
+ * This setting is immutable; attempts to clear the flag will
+ * cause a guard exception.
+ */
+int
+guarded_open_np(proc_t p, struct guarded_open_np_args *uap, int32_t *retval)
+{
+ if ((uap->flags & O_CLOEXEC) == 0)
+ return (EINVAL);
+
+#define GUARD_REQUIRED (GUARD_DUP)
+#define GUARD_ALL (GUARD_REQUIRED | \
+ (GUARD_CLOSE | GUARD_SOCKET_IPC | GUARD_FILEPORT))
+
+ if (((uap->guardflags & GUARD_REQUIRED) != GUARD_REQUIRED) ||
+ ((uap->guardflags & ~GUARD_ALL) != 0))
+ return (EINVAL);
+
+ int error;
+ struct gfp_crarg crarg = {
+ .gca_attrs = uap->guardflags
+ };
+
+ if ((error = copyin(uap->guard,
+ &(crarg.gca_guard), sizeof (crarg.gca_guard))) != 0)
+ return (error);
+
+ /*
+ * Disallow certain guard values -- is zero enough?
+ */
+ if (crarg.gca_guard == 0)
+ return (EINVAL);
+
+ struct filedesc *fdp = p->p_fd;
+ struct vnode_attr va;
+ struct nameidata nd;
+ vfs_context_t ctx = vfs_context_current();
+ int cmode;
+
+ VATTR_INIT(&va);
+ cmode = ((uap->mode & ~fdp->fd_cmask) & ALLPERMS) & ~S_ISTXT;
+ VATTR_SET(&va, va_mode, cmode & ACCESSPERMS);
+
+ NDINIT(&nd, LOOKUP, OP_OPEN, FOLLOW | AUDITVNPATH1, UIO_USERSPACE,
+ uap->path, ctx);
+
+ return (open1(ctx, &nd, uap->flags | O_CLOFORK, &va,
+ guarded_fileproc_alloc_init, &crarg, retval));
+}
+
+/*
+ * int guarded_kqueue_np(const guardid_t *guard, u_int guardflags);
+ *
+ * Create a guarded kqueue descriptor with guardid and guardflags.
+ *
+ * Same restrictions on guardflags as for guarded_open_np().
+ * All kqueues are -always- close-on-exec and close-on-fork by themselves.
+ *
+ * XXX Is it ever sensible to allow a kqueue fd (guarded or not) to
+ * be sent to another process via a fileport or socket?
+ */
+int
+guarded_kqueue_np(proc_t p, struct guarded_kqueue_np_args *uap, int32_t *retval)
+{
+ if (((uap->guardflags & GUARD_REQUIRED) != GUARD_REQUIRED) ||
+ ((uap->guardflags & ~GUARD_ALL) != 0))
+ return (EINVAL);
+
+ int error;
+ struct gfp_crarg crarg = {
+ .gca_attrs = uap->guardflags
+ };
+
+ if ((error = copyin(uap->guard,
+ &(crarg.gca_guard), sizeof (crarg.gca_guard))) != 0)
+ return (error);
+
+ if (crarg.gca_guard == 0)
+ return (EINVAL);
+
+ return (kqueue_body(p, guarded_fileproc_alloc_init, &crarg, retval));
+}
+
+/*
+ * int guarded_close_np(int fd, const guardid_t *guard);
+ */
+int
+guarded_close_np(proc_t p, struct guarded_close_np_args *uap,
+ __unused int32_t *retval)
+{
+ struct guarded_fileproc *gfp;
+ int fd = uap->fd;
+ int error;
+ guardid_t uguard;
+
+ AUDIT_SYSCLOSE(p, fd);
+
+ if ((error = copyin(uap->guard, &uguard, sizeof (uguard))) != 0)
+ return (error);
+
+ proc_fdlock(p);
+ if ((error = fp_lookup_guarded(p, fd, uguard, &gfp)) != 0) {
+ proc_fdunlock(p);
+ return (error);
+ }
+ error = close_internal_locked(p, fd, GFP_TO_FP(gfp), 0);
+ proc_fdunlock(p);
+ return (error);
+}
+
+/*
+ * int
+ * change_fdguard_np(int fd, const guardid_t *guard, u_int guardflags,
+ * const guardid_t *nguard, u_int nguardflags, int *fdflagsp);
+ *
+ * Given a file descriptor, atomically exchange <guard, guardflags> for
+ * a new guard <nguard, nguardflags>, returning the previous fd
+ * flags (see fcntl:F_SETFD) in *fdflagsp.
+ *
+ * This syscall can be used to either (a) add a new guard to an existing
+ * unguarded file descriptor (b) remove the old guard from an existing
+ * guarded file descriptor or (c) change the guard (guardid and/or
+ * guardflags) on a guarded file descriptor.
+ *
+ * If 'guard' is NULL, fd must be unguarded at entry. If the call completes
+ * successfully the fd will be guarded with <nguard, nguardflags>.
+ *
+ * Guarding a file descriptor has some side-effects on the "fdflags"
+ * associated with the descriptor - in particular FD_CLOEXEC is
+ * forced ON unconditionally, and FD_CLOFORK is forced ON by GUARD_CLOSE.
+ * Callers who wish to subsequently restore the state of the fd should save
+ * the value of *fdflagsp after a successful invocation.
+ *
+ * If 'nguard' is NULL, fd must be guarded at entry, <guard, guardflags>
+ * must match with what's already guarding the descriptor, and the
+ * result will be to completely remove the guard. Note also that the
+ * fdflags are copied to the descriptor from the incoming *fdflagsp argument.
+ *
+ * If the descriptor is guarded, and neither 'guard' nor 'nguard' is NULL
+ * and <guard, guardflags> matches what's already guarding the descriptor,
+ * then <nguard, nguardflags> becomes the new guard. In this case, even if
+ * the GUARD_CLOSE flag is being cleared, it is still possible to continue
+ * to keep FD_CLOFORK on the descriptor by passing FD_CLOFORK via fdflagsp.
+ *
+ * Example 1: Guard an unguarded descriptor during a set of operations,
+ * then restore the original state of the descriptor.
+ *
+ * int sav_flags = 0;
+ * change_fdguard_np(fd, NULL, 0, &myguard, GUARD_CLOSE, &sav_flags);
+ * // do things with now guarded 'fd'
+ * change_fdguard_np(fd, &myguard, GUARD_CLOSE, NULL, 0, &sav_flags);
+ * // fd now unguarded.
+ *
+ * Example 2: Change the guard of a guarded descriptor during a set of
+ * operations, then restore the original state of the descriptor.
+ *
+ * int sav_flags = (gdflags & GUARD_CLOSE) ? FD_CLOFORK : 0;
+ * change_fdguard_np(fd, &gd, gdflags, &myguard, GUARD_CLOSE, &sav_flags);
+ * // do things with 'fd' with a different guard
+ * change_fdguard_np(fd, &myg, GUARD_CLOSE, &gd, gdflags, &sav_flags);
+ * // back to original guarded state
+ */
+
+#define FDFLAGS_GET(p, fd) (*fdflags(p, fd) & (UF_EXCLOSE|UF_FORKCLOSE))
+#define FDFLAGS_SET(p, fd, bits) \
+ (*fdflags(p, fd) |= ((bits) & (UF_EXCLOSE|UF_FORKCLOSE)))
+#define FDFLAGS_CLR(p, fd, bits) \
+ (*fdflags(p, fd) &= ~((bits) & (UF_EXCLOSE|UF_FORKCLOSE)))
+
+int
+change_fdguard_np(proc_t p, struct change_fdguard_np_args *uap,
+ __unused int32_t *retval)
+{
+ struct fileproc *fp;
+ int fd = uap->fd;
+ int error;
+ guardid_t oldg = 0, newg = 0;
+ int nfdflags = 0;
+
+ if (0 != uap->guard &&
+ 0 != (error = copyin(uap->guard, &oldg, sizeof (oldg))))
+ return (error); /* can't copyin current guard */
+
+ if (0 != uap->nguard &&
+ 0 != (error = copyin(uap->nguard, &newg, sizeof (newg))))
+ return (error); /* can't copyin new guard */
+
+ if (0 != uap->fdflagsp &&
+ 0 != (error = copyin(uap->fdflagsp, &nfdflags, sizeof (nfdflags))))
+ return (error); /* can't copyin new fdflags */
+
+ proc_fdlock(p);
+restart:
+ if ((error = fp_lookup(p, fd, &fp, 1)) != 0) {
+ proc_fdunlock(p);
+ return (error);
+ }
+
+ if (0 != uap->fdflagsp) {
+ int ofdflags = FDFLAGS_GET(p, fd);
+ int ofl = ((ofdflags & UF_EXCLOSE) ? FD_CLOEXEC : 0) |
+ ((ofdflags & UF_FORKCLOSE) ? FD_CLOFORK : 0);
+ proc_fdunlock(p);
+ if (0 != (error = copyout(&ofl, uap->fdflagsp, sizeof (ofl)))) {
+ proc_fdlock(p);
+ goto dropout; /* can't copyout old fdflags */
+ }
+ proc_fdlock(p);
+ }
+
+ if (FILEPROC_TYPE(fp) == FTYPE_GUARDED) {
+ if (0 == uap->guard || 0 == uap->guardflags)
+ error = EINVAL; /* missing guard! */
+ else if (0 == oldg)
+ error = EPERM; /* guardids cannot be zero */
+ } else {
+ if (0 != uap->guard || 0 != uap->guardflags)
+ error = EINVAL; /* guard provided, but none needed! */
+ }
+
+ if (0 != error)
+ goto dropout;
+
+ if (0 != uap->nguard) {
+ /*
+ * There's a new guard in town.
+ */
+ if (0 == newg)
+ error = EINVAL; /* guards cannot contain zero */
+ else if (0 == uap->nguardflags)
+ error = EINVAL; /* attributes cannot be zero */
+ else if (((uap->nguardflags & GUARD_REQUIRED) != GUARD_REQUIRED) ||
+ ((uap->guardflags & ~GUARD_ALL) != 0))
+ error = EINVAL; /* must have valid attributes too */
+
+ if (0 != error)
+ goto dropout;
+
+ if (FILEPROC_TYPE(fp) == FTYPE_GUARDED) {
+ /*
+ * Replace old guard with new guard
+ */
+ struct guarded_fileproc *gfp = FP_TO_GFP(fp);
+
+ if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
+ panic("%s: corrupt gfp %p flags %x",
+ __func__, gfp, fp->f_flags);
+
+ if (oldg == gfp->gf_guard &&
+ uap->guardflags == gfp->gf_attrs) {
+ /*
+ * Must match existing guard + attributes
+ * before we'll swap them to new ones, managing
+ * fdflags "side-effects" as we go. Note that
+ * userland can request FD_CLOFORK semantics.
+ */
+ if (gfp->gf_attrs & GUARD_CLOSE)
+ FDFLAGS_CLR(p, fd, UF_FORKCLOSE);
+ gfp->gf_guard = newg;
+ gfp->gf_attrs = uap->nguardflags;
+ if (gfp->gf_attrs & GUARD_CLOSE)
+ FDFLAGS_SET(p, fd, UF_FORKCLOSE);
+ FDFLAGS_SET(p, fd,
+ (nfdflags & FD_CLOFORK) ? UF_FORKCLOSE : 0);
+ } else {
+ error = EPERM;
+ }
+ goto dropout;
+ } else {
+ /*
+ * Add a guard to a previously unguarded descriptor
+ */
+ switch (FILEGLOB_DTYPE(fp->f_fglob)) {
+ case DTYPE_VNODE:
+ case DTYPE_PIPE:
+ case DTYPE_SOCKET:
+ case DTYPE_KQUEUE:
+ break;
+ default:
+ error = ENOTSUP;
+ goto dropout;
+ }
+
+ proc_fdunlock(p);
+
+ struct gfp_crarg crarg = {
+ .gca_guard = newg,
+ .gca_attrs = uap->nguardflags
+ };
+ struct fileproc *nfp =
+ guarded_fileproc_alloc_init(&crarg);
+
+ proc_fdlock(p);
+
+ switch (error = fp_tryswap(p, fd, nfp)) {
+ struct guarded_fileproc *gfp;
+
+ case 0: /* guarded-ness comes with side-effects */
+ gfp = FP_TO_GFP(nfp);
+ if (gfp->gf_attrs & GUARD_CLOSE)
+ FDFLAGS_SET(p, fd, UF_FORKCLOSE);
+ FDFLAGS_SET(p, fd, UF_EXCLOSE);
+ (void) fp_drop(p, fd, nfp, 1);
+ fileproc_free(fp);
+ break;
+ case EKEEPLOOKING: /* f_iocount indicates a collision */
+ (void) fp_drop(p, fd, fp, 1);
+ fileproc_free(nfp);
+ goto restart;
+ default:
+ (void) fp_drop(p, fd, fp, 1);
+ fileproc_free(nfp);
+ break;
+ }
+ proc_fdunlock(p);
+ return (error);
+ }
+ } else {
+ /*
+ * No new guard.
+ */
+ if (FILEPROC_TYPE(fp) == FTYPE_GUARDED) {
+ /*
+ * Remove the guard altogether.
+ */
+ struct guarded_fileproc *gfp = FP_TO_GFP(fp);
+
+ if (0 != uap->nguardflags) {
+ error = EINVAL;
+ goto dropout;
+ }
+
+ if (GUARDED_FILEPROC_MAGIC != gfp->gf_magic)
+ panic("%s: corrupt gfp %p flags %x",
+ __func__, gfp, fp->f_flags);
+
+ if (oldg != gfp->gf_guard ||
+ uap->guardflags != gfp->gf_attrs) {
+ error = EPERM;
+ goto dropout;
+ }
+
+ proc_fdunlock(p);
+ struct fileproc *nfp = fileproc_alloc_init(NULL);
+ proc_fdlock(p);
+
+ switch (error = fp_tryswap(p, fd, nfp)) {
+ case 0: /* undo side-effects of guarded-ness */
+ FDFLAGS_CLR(p, fd, UF_FORKCLOSE | UF_EXCLOSE);
+ FDFLAGS_SET(p, fd,
+ (nfdflags & FD_CLOFORK) ? UF_FORKCLOSE : 0);
+ FDFLAGS_SET(p, fd,
+ (nfdflags & FD_CLOEXEC) ? UF_EXCLOSE : 0);
+ (void) fp_drop(p, fd, nfp, 1);
+ fileproc_free(fp);
+ break;
+ case EKEEPLOOKING: /* f_iocount indicates collision */
+ (void) fp_drop(p, fd, fp, 1);
+ fileproc_free(nfp);
+ goto restart;
+ default:
+ (void) fp_drop(p, fd, fp, 1);
+ fileproc_free(nfp);
+ break;
+ }
+ proc_fdunlock(p);
+ return (error);
+ } else {
+ /*
+ * Not already guarded, and no new guard?
+ */
+ error = EINVAL;
+ }
+ }
+
+dropout:
+ (void) fp_drop(p, fd, fp, 1);
+ proc_fdunlock(p);
+ return (error);
+}
+