/*
- * Copyright (c) 2000-2014 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2016 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
* 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,
* 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@
*/
/* Copyright (c) 1995, 1997 Apple Computer, Inc. All Rights Reserved */
#include <sys/kauth.h>
#include <sys/file_internal.h>
#include <sys/guarded.h>
+#include <sys/priv.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/sysproto.h>
#include <sys/pipe.h>
#include <sys/spawn.h>
+#include <sys/cprotect.h>
#include <kern/kern_types.h>
#include <kern/kalloc.h>
+#include <kern/waitq.h>
#include <libkern/OSAtomic.h>
#include <sys/ubc_internal.h>
#include <mach/mach_port.h>
#include <stdbool.h>
-#if CONFIG_PROTECT
-#include <sys/cprotect.h>
-#endif
-#include <hfs/hfs.h>
-
kern_return_t ipc_object_copyin(ipc_space_t, mach_port_name_t,
mach_msg_type_name_t, ipc_port_t *);
void ipc_port_release_send(ipc_port_t);
extern kauth_scope_t kauth_scope_fileop;
/* Conflict wait queue for when selects collide (opaque type) */
-extern struct wait_queue select_conflict_queue;
+extern struct waitq select_conflict_queue;
#define f_flag f_fglob->fg_flag
#define f_type f_fglob->fg_ops->fo_type
return EINVAL;
}
/* Check if end marker is beyond LLONG_MAX. */
- if ((fl->l_len > 0) && (CHECK_ADD_OVERFLOW_INT64L(fl->l_start +
+ if ((fl->l_len > 0) && (CHECK_ADD_OVERFLOW_INT64L(fl->l_start +
cur_file_offset, fl->l_len - 1))) {
return EOVERFLOW;
}
return EINVAL;
}
/* Check if the end marker is beyond LLONG_MAX. */
- if ((fl->l_len > 0) &&
+ if ((fl->l_len > 0) &&
CHECK_ADD_OVERFLOW_INT64L(fl->l_start, fl->l_len - 1)) {
return EOVERFLOW;
}
}
}
-void
+void
procfdtbl_waitfd(struct proc * p, int fd)
{
p->p_fd->fd_ofileflags[fd] |= UF_RESVWAIT;
int waiting;
waiting = (p->p_fd->fd_ofileflags[fd] & UF_RESVWAIT);
- p->p_fd->fd_ofiles[fd] = NULL;
+ p->p_fd->fd_ofiles[fd] = NULL;
p->p_fd->fd_ofileflags[fd] = 0;
if ( waiting == UF_RESVWAIT) {
wakeup(&p->p_fd);
error = EBADF;
goto out;
}
-
+
if (rw == UIO_READ && !(fp->f_flag & FREAD)) {
error = EBADF;
goto out;
}
-
+
context.vc_ucred = fp->f_fglob->fg_cred;
if (UIO_SEG_IS_USER_SPACE(segflg))
fp_drop(p, old, fp, 1);
proc_fdunlock(p);
+ if (ENTR_SHOULDTRACE && fp->f_type == DTYPE_SOCKET) {
+ KERNEL_ENERGYTRACE(kEnTrActKernSocket, DBG_FUNC_START,
+ new, 0, (int64_t)VM_KERNEL_ADDRPERM(fp->f_data));
+ }
+
return (error);
}
struct fileproc *fp;
char *pop;
struct vnode *vp = NULLVP; /* for AUDIT_ARG() at end */
- int i, tmp, error, error2, flg = F_POSIX;
+ int i, tmp, error, error2, flg = 0;
struct flock fl;
struct flocktimeout fltimeout;
struct timespec *timeout = NULL;
}
goto out;
+ case F_SETCONFINED:
+ /*
+ * If this is the only reference to this fglob in the process
+ * and it's already marked as close-on-fork then mark it as
+ * (immutably) "confined" i.e. any fd that points to it will
+ * forever be close-on-fork, and attempts to use an IPC
+ * mechanism to move the descriptor elsewhere will fail.
+ */
+ if (CAST_DOWN_EXPLICIT(int, uap->arg)) {
+ struct fileglob *fg = fp->f_fglob;
+
+ lck_mtx_lock_spin(&fg->fg_lock);
+ if (fg->fg_lflags & FG_CONFINED)
+ error = 0;
+ else if (1 != fg->fg_count)
+ error = EAGAIN; /* go close the dup .. */
+ else if (UF_FORKCLOSE == (*pop & UF_FORKCLOSE)) {
+ fg->fg_lflags |= FG_CONFINED;
+ error = 0;
+ } else
+ error = EBADF; /* open without O_CLOFORK? */
+ lck_mtx_unlock(&fg->fg_lock);
+ } else {
+ /*
+ * Other subsystems may have built on the immutability
+ * of FG_CONFINED; clearing it may be tricky.
+ */
+ error = EPERM; /* immutable */
+ }
+ goto out;
+
+ case F_GETCONFINED:
+ *retval = (fp->f_fglob->fg_lflags & FG_CONFINED) ? 1 : 0;
+ error = 0;
+ goto out;
+
case F_SETLKWTIMEOUT:
case F_SETLKW:
+ case F_OFD_SETLKWTIMEOUT:
+ case F_OFD_SETLKW:
flg |= F_WAIT;
/* Fall into F_SETLK */
case F_SETLK:
+ case F_OFD_SETLK:
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
goto out;
proc_fdunlock(p);
/* Copy in the lock structure */
- if (uap->cmd == F_SETLKWTIMEOUT) {
+ if (F_SETLKWTIMEOUT == uap->cmd ||
+ F_OFD_SETLKWTIMEOUT == uap->cmd) {
error = copyin(argp, (caddr_t) &fltimeout, sizeof(fltimeout));
if (error) {
goto outdrop;
goto outdrop;
}
#endif
- switch (fl.l_type) {
-
- case F_RDLCK:
- if ((fflag & FREAD) == 0) {
- (void)vnode_put(vp);
- error = EBADF;
- goto outdrop;
+ switch (uap->cmd) {
+ case F_OFD_SETLK:
+ case F_OFD_SETLKW:
+ case F_OFD_SETLKWTIMEOUT:
+ flg |= F_OFD_LOCK;
+ switch (fl.l_type) {
+ case F_RDLCK:
+ if ((fflag & FREAD) == 0) {
+ error = EBADF;
+ break;
+ }
+ error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob,
+ F_SETLK, &fl, flg, &context, timeout);
+ break;
+ case F_WRLCK:
+ if ((fflag & FWRITE) == 0) {
+ error = EBADF;
+ break;
+ }
+ error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob,
+ F_SETLK, &fl, flg, &context, timeout);
+ break;
+ case F_UNLCK:
+ error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob,
+ F_UNLCK, &fl, F_OFD_LOCK, &context,
+ timeout);
+ break;
+ default:
+ error = EINVAL;
+ break;
}
- // XXX UInt32 unsafe for LP64 kernel
- OSBitOrAtomic(P_LADVLOCK, &p->p_ladvflag);
- error = VNOP_ADVLOCK(vp, (caddr_t)p, F_SETLK, &fl, flg, &context, timeout);
- (void)vnode_put(vp);
- goto outdrop;
+ if (0 == error &&
+ (F_RDLCK == fl.l_type || F_WRLCK == fl.l_type)) {
+ struct fileglob *fg = fp->f_fglob;
- case F_WRLCK:
- if ((fflag & FWRITE) == 0) {
- (void)vnode_put(vp);
- error = EBADF;
- goto outdrop;
+ /*
+ * arrange F_UNLCK on last close (once
+ * set, FG_HAS_OFDLOCK is immutable)
+ */
+ if ((fg->fg_lflags & FG_HAS_OFDLOCK) == 0) {
+ lck_mtx_lock_spin(&fg->fg_lock);
+ fg->fg_lflags |= FG_HAS_OFDLOCK;
+ lck_mtx_unlock(&fg->fg_lock);
+ }
}
- // XXX UInt32 unsafe for LP64 kernel
- OSBitOrAtomic(P_LADVLOCK, &p->p_ladvflag);
- error = VNOP_ADVLOCK(vp, (caddr_t)p, F_SETLK, &fl, flg, &context, timeout);
- (void)vnode_put(vp);
- goto outdrop;
-
- case F_UNLCK:
- error = VNOP_ADVLOCK(vp, (caddr_t)p, F_UNLCK, &fl,
- F_POSIX, &context, timeout);
- (void)vnode_put(vp);
- goto outdrop;
-
+ break;
default:
- (void)vnode_put(vp);
- error = EINVAL;
- goto outdrop;
+ flg |= F_POSIX;
+ switch (fl.l_type) {
+ case F_RDLCK:
+ if ((fflag & FREAD) == 0) {
+ error = EBADF;
+ break;
+ }
+ // XXX UInt32 unsafe for LP64 kernel
+ OSBitOrAtomic(P_LADVLOCK, &p->p_ladvflag);
+ error = VNOP_ADVLOCK(vp, (caddr_t)p,
+ F_SETLK, &fl, flg, &context, timeout);
+ break;
+ case F_WRLCK:
+ if ((fflag & FWRITE) == 0) {
+ error = EBADF;
+ break;
+ }
+ // XXX UInt32 unsafe for LP64 kernel
+ OSBitOrAtomic(P_LADVLOCK, &p->p_ladvflag);
+ error = VNOP_ADVLOCK(vp, (caddr_t)p,
+ F_SETLK, &fl, flg, &context, timeout);
+ break;
+ case F_UNLCK:
+ error = VNOP_ADVLOCK(vp, (caddr_t)p,
+ F_UNLCK, &fl, F_POSIX, &context, timeout);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
}
+ (void) vnode_put(vp);
+ goto outdrop;
case F_GETLK:
+ case F_OFD_GETLK:
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
goto out;
uap->cmd, &fl);
if (error == 0)
#endif
- error = VNOP_ADVLOCK(vp, (caddr_t)p, uap->cmd, &fl, F_POSIX, &context, NULL);
+ switch (uap->cmd) {
+ case F_OFD_GETLK:
+ error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob,
+ F_GETLK, &fl, F_OFD_LOCK, &context, NULL);
+ break;
+ case F_OFD_GETLKPID:
+ error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob,
+ F_GETLKPID, &fl, F_OFD_LOCK, &context, NULL);
+ break;
+ default:
+ error = VNOP_ADVLOCK(vp, (caddr_t)p,
+ uap->cmd, &fl, F_POSIX, &context, NULL);
+ break;
+ }
(void)vnode_put(vp);
/* now set the space allocated to 0 */
alloc_struct.fst_bytesalloc = 0;
-
+
/*
* Do some simple parameter checking
*/
/* set up the flags */
alloc_flags |= PREALLOCATE;
-
+
if (alloc_struct.fst_flags & F_ALLOCATECONTIG)
alloc_flags |= ALLOCATECONTIG;
*/
switch (alloc_struct.fst_posmode) {
-
+
case F_PEOFPOSMODE:
if (alloc_struct.fst_offset != 0) {
error = EINVAL;
error = error2;
}
goto outdrop;
-
+ }
+ case F_PUNCHHOLE: {
+ fpunchhole_t args;
+
+ if (fp->f_type != DTYPE_VNODE) {
+ error = EBADF;
+ goto out;
+ }
+
+ vp = (struct vnode *)fp->f_data;
+ proc_fdunlock(p);
+
+ /* need write permissions */
+ if ((fp->f_flag & FWRITE) == 0) {
+ error = EPERM;
+ goto outdrop;
}
+
+ if ((error = copyin(argp, (caddr_t)&args, sizeof(args)))) {
+ goto outdrop;
+ }
+
+ if ((error = vnode_getwithref(vp))) {
+ goto outdrop;
+ }
+
+#if CONFIG_MACF
+ if ((error = mac_vnode_check_write(&context, fp->f_fglob->fg_cred, vp))) {
+ (void)vnode_put(vp);
+ goto outdrop;
+ }
+#endif
+
+ error = VNOP_IOCTL(vp, F_PUNCHHOLE, (caddr_t)&args, 0, &context);
+ (void)vnode_put(vp);
+
+ goto outdrop;
+ }
+ case F_TRIM_ACTIVE_FILE: {
+ ftrimactivefile_t args;
+
+ if (priv_check_cred(kauth_cred_get(), PRIV_TRIM_ACTIVE_FILE, 0)) {
+ error = EACCES;
+ goto out;
+ }
+
+ if (fp->f_type != DTYPE_VNODE) {
+ error = EBADF;
+ goto out;
+ }
+
+ vp = (struct vnode *)fp->f_data;
+ proc_fdunlock(p);
+
+ /* need write permissions */
+ if ((fp->f_flag & FWRITE) == 0) {
+ error = EPERM;
+ goto outdrop;
+ }
+
+ if ((error = copyin(argp, (caddr_t)&args, sizeof(args)))) {
+ goto outdrop;
+ }
+
+ if ((error = vnode_getwithref(vp))) {
+ goto outdrop;
+ }
+
+ error = VNOP_IOCTL(vp, F_TRIM_ACTIVE_FILE, (caddr_t)&args, 0, &context);
+ (void)vnode_put(vp);
+
+ goto outdrop;
+ }
case F_SETSIZE:
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
#endif
/*
* Make sure that we are root. Growing a file
- * without zero filling the data is a security hole
+ * without zero filling the data is a security hole
* root would have access anyway so we'll allow it
*/
if (!kauth_cred_issuser(kauth_cred_get())) {
*/
error = vnode_setsize(vp, offset, IO_NOZEROFILL,
&context);
+
+#if CONFIG_MACF
+ if (error == 0)
+ mac_vnode_notify_truncate(&context, fp->f_fglob->fg_cred, vp);
+#endif
}
(void)vnode_put(vp);
proc_fdunlock(p);
if ( (error = vnode_getwithref(vp)) == 0 ) {
- error = cluster_push(vp, 0);
+ error = VNOP_FSYNC(vp, MNT_NOWAIT, &context);
(void)vnode_put(vp);
}
}
devBlockSize = vfs_devblocksize(vnode_mount(vp));
if (uap->cmd == F_LOG2PHYS_EXT) {
-#if defined(__LP64__)
- a_size = l2p_struct.l2p_contigbytes;
-#else
- if ((l2p_struct.l2p_contigbytes > SIZE_MAX) || (l2p_struct.l2p_contigbytes < 0)) {
- /* size_t is 32-bit on a 32-bit kernel, therefore
- * assigning l2p_contigbytes to a_size may have
- * caused integer overflow. We, therefore, return
- * an error here instead of calculating incorrect
- * value.
- */
- printf ("fcntl: F_LOG2PHYS_EXT: l2p_contigbytes=%lld will overflow, returning error\n", l2p_struct.l2p_contigbytes);
- error = EFBIG;
+ if (l2p_struct.l2p_contigbytes < 0) {
+ vnode_put(vp);
+ error = EINVAL;
goto outdrop;
- } else {
- a_size = l2p_struct.l2p_contigbytes;
}
-#endif
+
+ a_size = MIN((uint64_t)l2p_struct.l2p_contigbytes, SIZE_MAX);
} else {
a_size = devBlockSize;
}
-
+
error = VNOP_BLOCKMAP(vp, offset, a_size, &bn, &run, NULL, 0, &context);
(void)vnode_put(vp);
}
case F_CHKCLEAN: // used by regression tests to see if all dirty pages got cleaned by fsync()
- case F_FULLFSYNC: // fsync + flush the journal + DKIOCSYNCHRONIZECACHE
+ case F_FULLFSYNC: // fsync + flush the journal + DKIOCSYNCHRONIZE
+ case F_BARRIERFSYNC: // fsync + barrier
case F_FREEZE_FS: // freeze all other fs operations for the fs of this fd
case F_THAW_FS: { // thaw all frozen fs operations for the fs of this fd
if (fp->f_type != DTYPE_VNODE) {
error = ENOENT;
goto outdrop;
}
-
+
/* Only valid for directories */
if (vp->v_type != VDIR) {
vnode_put(vp);
error = ENOENT;
goto outdrop;
}
-
+
/* Only valid for directories */
if (vp->v_type != VDIR) {
vnode_put(vp);
/* Start the lookup relative to the file descriptor's vnode. */
error = unlink1(&context, vp, pathname, UIO_USERSPACE, 0);
-
+
vnode_put(vp);
break;
case F_ADDSIGS:
case F_ADDFILESIGS:
case F_ADDFILESIGS_FOR_DYLD_SIM:
+ case F_ADDFILESIGS_RETURN:
{
+ struct cs_blob *blob = NULL;
struct user_fsignatures fs;
kern_return_t kr;
vm_offset_t kernel_blob_addr;
goto outdrop;
}
- struct cs_blob * existing_blob = ubc_cs_blob_get(vp, CPU_TYPE_ANY, fs.fs_file_start);
- if (existing_blob != NULL)
+ /*
+ * First check if we have something loaded a this offset
+ */
+ blob = ubc_cs_blob_get(vp, CPU_TYPE_ANY, fs.fs_file_start);
+ if (blob != NULL)
{
/* If this is for dyld_sim revalidate the blob */
if (uap->cmd == F_ADDFILESIGS_FOR_DYLD_SIM) {
- error = ubc_cs_blob_revalidate(vp, existing_blob, blob_add_flags);
+ error = ubc_cs_blob_revalidate(vp, blob, NULL, blob_add_flags);
+ if (error) {
+ vnode_put(vp);
+ goto outdrop;
+ }
}
- vnode_put(vp);
- goto outdrop;
- }
-/*
- * An arbitrary limit, to prevent someone from mapping in a 20GB blob. This should cover
- * our use cases for the immediate future, but note that at the time of this commit, some
- * platforms are nearing 2MB blob sizes (with a prior soft limit of 2.5MB).
- *
- * We should consider how we can manage this more effectively; the above means that some
- * platforms are using megabytes of memory for signing data; it merely hasn't crossed the
- * threshold considered ridiculous at the time of this change.
- */
-#define CS_MAX_BLOB_SIZE (10ULL * 1024ULL * 1024ULL)
- if (fs.fs_blob_size > CS_MAX_BLOB_SIZE) {
- error = E2BIG;
- vnode_put(vp);
- goto outdrop;
- }
- kernel_blob_size = CAST_DOWN(vm_size_t, fs.fs_blob_size);
- kr = ubc_cs_blob_allocate(&kernel_blob_addr, &kernel_blob_size);
- if (kr != KERN_SUCCESS) {
- error = ENOMEM;
- vnode_put(vp);
- goto outdrop;
- }
+ } else {
+ /*
+ * An arbitrary limit, to prevent someone from mapping in a 20GB blob. This should cover
+ * our use cases for the immediate future, but note that at the time of this commit, some
+ * platforms are nearing 2MB blob sizes (with a prior soft limit of 2.5MB).
+ *
+ * We should consider how we can manage this more effectively; the above means that some
+ * platforms are using megabytes of memory for signing data; it merely hasn't crossed the
+ * threshold considered ridiculous at the time of this change.
+ */
+#define CS_MAX_BLOB_SIZE (40ULL * 1024ULL * 1024ULL)
+ if (fs.fs_blob_size > CS_MAX_BLOB_SIZE) {
+ error = E2BIG;
+ vnode_put(vp);
+ goto outdrop;
+ }
- if(uap->cmd == F_ADDSIGS) {
- error = copyin(fs.fs_blob_start,
- (void *) kernel_blob_addr,
- kernel_blob_size);
- } else /* F_ADDFILESIGS */ {
- int resid;
-
- error = vn_rdwr(UIO_READ,
- vp,
- (caddr_t) kernel_blob_addr,
- kernel_blob_size,
- fs.fs_file_start + fs.fs_blob_start,
- UIO_SYSSPACE,
- 0,
- kauth_cred_get(),
- &resid,
- p);
- if ((error == 0) && resid) {
- /* kernel_blob_size rounded to a page size, but signature may be at end of file */
- memset((void *)(kernel_blob_addr + (kernel_blob_size - resid)), 0x0, resid);
+ kernel_blob_size = CAST_DOWN(vm_size_t, fs.fs_blob_size);
+ kr = ubc_cs_blob_allocate(&kernel_blob_addr, &kernel_blob_size);
+ if (kr != KERN_SUCCESS) {
+ error = ENOMEM;
+ vnode_put(vp);
+ goto outdrop;
}
- }
-
- if (error) {
- ubc_cs_blob_deallocate(kernel_blob_addr,
- kernel_blob_size);
- vnode_put(vp);
- goto outdrop;
- }
- error = ubc_cs_blob_add(
- vp,
- CPU_TYPE_ANY, /* not for a specific architecture */
- fs.fs_file_start,
- kernel_blob_addr,
- kernel_blob_size,
- blob_add_flags);
- if (error) {
- ubc_cs_blob_deallocate(kernel_blob_addr,
+ if(uap->cmd == F_ADDSIGS) {
+ error = copyin(fs.fs_blob_start,
+ (void *) kernel_blob_addr,
kernel_blob_size);
- } else {
- /* ubc_blob_add() has consumed "kernel_blob_addr" */
+ } else /* F_ADDFILESIGS || F_ADDFILESIGS_RETURN || F_ADDFILESIGS_FOR_DYLD_SIM */ {
+ int resid;
+
+ error = vn_rdwr(UIO_READ,
+ vp,
+ (caddr_t) kernel_blob_addr,
+ kernel_blob_size,
+ fs.fs_file_start + fs.fs_blob_start,
+ UIO_SYSSPACE,
+ 0,
+ kauth_cred_get(),
+ &resid,
+ p);
+ if ((error == 0) && resid) {
+ /* kernel_blob_size rounded to a page size, but signature may be at end of file */
+ memset((void *)(kernel_blob_addr + (kernel_blob_size - resid)), 0x0, resid);
+ }
+ }
+
+ if (error) {
+ ubc_cs_blob_deallocate(kernel_blob_addr,
+ kernel_blob_size);
+ vnode_put(vp);
+ goto outdrop;
+ }
+
+ blob = NULL;
+ error = ubc_cs_blob_add(vp,
+ CPU_TYPE_ANY, /* not for a specific architecture */
+ fs.fs_file_start,
+ &kernel_blob_addr,
+ kernel_blob_size,
+ NULL,
+ blob_add_flags,
+ &blob);
+
+ /* ubc_blob_add() has consumed "kernel_blob_addr" if it is zeroed */
+ if (error) {
+ if (kernel_blob_addr) {
+ ubc_cs_blob_deallocate(kernel_blob_addr,
+ kernel_blob_size);
+ }
+ vnode_put(vp);
+ goto outdrop;
+ } else {
#if CHECK_CS_VALIDATION_BITMAP
- ubc_cs_validation_bitmap_allocate( vp );
+ ubc_cs_validation_bitmap_allocate( vp );
#endif
+ }
+ }
+
+ if (uap->cmd == F_ADDFILESIGS_RETURN || uap->cmd == F_ADDFILESIGS_FOR_DYLD_SIM) {
+ /*
+ * The first element of the structure is a
+ * off_t that happen to have the same size for
+ * all archs. Lets overwrite that.
+ */
+ off_t end_offset = 0;
+ if (blob)
+ end_offset = blob->csb_end_offset;
+ error = copyout(&end_offset, argp, sizeof (end_offset));
}
(void) vnode_put(vp);
break;
}
+ case F_GETCODEDIR:
case F_FINDSIGS: {
-#ifdef SECURE_KERNEL
error = ENOTSUP;
-#else /* !SECURE_KERNEL */
- off_t offsetMacho;
+ goto out;
+ }
+ case F_CHECK_LV: {
+ struct fileglob *fg;
+ fchecklv_t lv;
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
goto out;
}
- vp = (struct vnode *)fp->f_data;
+ fg = fp->f_fglob;
proc_fdunlock(p);
- error = vnode_getwithref(vp);
- if (error)
- goto outdrop;
- error = copyin(argp, &offsetMacho, sizeof(offsetMacho));
- if (error) {
- (void)vnode_put(vp);
- goto outdrop;
+ if (IS_64BIT_PROCESS(p)) {
+ error = copyin(argp, &lv, sizeof (lv));
+ } else {
+ struct user32_fchecklv lv32;
+
+ error = copyin(argp, &lv32, sizeof (lv32));
+ lv.lv_file_start = lv32.lv_file_start;
+ lv.lv_error_message = CAST_USER_ADDR_T(lv32.lv_error_message);
+ lv.lv_error_message_size = lv32.lv_error_message;
}
+ if (error)
+ goto outdrop;
#if CONFIG_MACF
- error = mac_vnode_find_sigs(p, vp, offsetMacho);
-#else
- error = EPERM;
+ error = mac_file_check_library_validation(p, fg, lv.lv_file_start,
+ lv.lv_error_message, lv.lv_error_message_size);
#endif
- if (error) {
- (void)vnode_put(vp);
- goto outdrop;
- }
-#endif /* SECURE_KERNEL */
+
break;
}
#if CONFIG_PROTECT
case F_GETPROTECTIONCLASS: {
- int class = 0;
-
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
goto out;
error = ENOENT;
goto outdrop;
}
-
- error = cp_vnode_getclass (vp, &class);
- if (error == 0) {
- *retval = class;
+
+ struct vnode_attr va;
+
+ VATTR_INIT(&va);
+ VATTR_WANTED(&va, va_dataprotect_class);
+ error = VNOP_GETATTR(vp, &va, &context);
+ if (!error) {
+ if (VATTR_IS_SUPPORTED(&va, va_dataprotect_class))
+ *retval = va.va_dataprotect_class;
+ else
+ error = ENOTSUP;
}
vnode_put(vp);
break;
}
-
+
case F_SETPROTECTIONCLASS: {
/* tmp must be a valid PROTECTION_CLASS_* */
tmp = CAST_DOWN_EXPLICIT(uint32_t, uap->arg);
-
+
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
goto out;
vp = (struct vnode *)fp->f_data;
proc_fdunlock(p);
-
+
if (vnode_getwithref(vp)) {
error = ENOENT;
goto outdrop;
- }
-
+ }
+
/* Only go forward if you have write access */
vfs_context_t ctx = vfs_context_current();
if(vnode_authorize(vp, NULLVP, (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA), ctx) != 0) {
error = EBADF;
goto outdrop;
}
- error = cp_vnode_setclass (vp, tmp);
+
+ struct vnode_attr va;
+
+ VATTR_INIT(&va);
+ VATTR_SET(&va, va_dataprotect_class, tmp);
+
+ error = VNOP_SETATTR(vp, &va, ctx);
+
vnode_put(vp);
break;
- }
+ }
case F_TRANSCODEKEY: {
-
- char *backup_keyp = NULL;
- unsigned backup_key_len = CP_MAX_WRAPPEDKEYSIZE;
-
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
goto out;
}
-
+
vp = (struct vnode *)fp->f_data;
proc_fdunlock(p);
if (vnode_getwithref(vp)) {
error = ENOENT;
goto outdrop;
- }
-
- MALLOC(backup_keyp, char *, backup_key_len, M_TEMP, M_WAITOK);
- if (backup_keyp == NULL) {
- error = ENOMEM;
- goto outdrop;
}
- error = cp_vnode_transcode (vp, backup_keyp, &backup_key_len);
+ cp_key_t k = {
+ .len = CP_MAX_WRAPPEDKEYSIZE,
+ };
+
+ MALLOC(k.key, char *, k.len, M_TEMP, M_WAITOK);
+
+ error = VNOP_IOCTL(vp, F_TRANSCODEKEY, (caddr_t)&k, 1, &context);
+
vnode_put(vp);
if (error == 0) {
- error = copyout((caddr_t)backup_keyp, argp, backup_key_len);
- *retval = backup_key_len;
+ error = copyout(k.key, argp, k.len);
+ *retval = k.len;
}
- FREE(backup_keyp, M_TEMP);
+ FREE(k.key, M_TEMP);
break;
- }
+ }
case F_GETPROTECTIONLEVEL: {
- uint32_t cp_version = 0;
-
if (fp->f_type != DTYPE_VNODE) {
- error = EBADF;
+ error = EBADF;
goto out;
}
goto outdrop;
}
- /*
- * if cp_get_major_vers fails, error will be set to proper errno
- * and cp_version will still be 0.
- */
-
- error = cp_get_root_major_vers (vp, &cp_version);
- *retval = cp_version;
+ error = VNOP_IOCTL(vp, F_GETPROTECTIONLEVEL, (caddr_t)retval, 0, &context);
vnode_put (vp);
break;
}
case F_GETDEFAULTPROTLEVEL: {
- uint32_t cp_default = 0;
-
if (fp->f_type != DTYPE_VNODE) {
- error = EBADF;
+ error = EBADF;
goto out;
}
}
/*
- * if cp_get_major_vers fails, error will be set to proper errno
+ * if cp_get_major_vers fails, error will be set to proper errno
* and cp_version will still be 0.
*/
- error = cp_get_default_level(vp, &cp_default);
- *retval = cp_default;
+ error = VNOP_IOCTL(vp, F_GETDEFAULTPROTLEVEL, (caddr_t)retval, 0, &context);
vnode_put (vp);
break;
}
-
#endif /* CONFIG_PROTECT */
case F_MOVEDATAEXTENTS: {
struct vnode *dst_vp = NULLVP;
/* We need to grab the 2nd FD out of the argments before moving on. */
int fd2 = CAST_DOWN_EXPLICIT(int32_t, uap->arg);
-
+
+ error = priv_check_cred(kauth_cred_get(), PRIV_VFS_MOVE_DATA_EXTENTS, 0);
+ if (error)
+ goto out;
+
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
goto out;
}
/*
- * Get the references before we start acquiring iocounts on the vnodes,
+ * Get the references before we start acquiring iocounts on the vnodes,
* while we still hold the proc fd lock
*/
if ( (error = fp_lookup(p, fd2, &fp2, 1)) ) {
fp_drop(p, fd2, fp2, 0);
error = ENOENT;
goto outdrop;
- }
+ }
if (vnode_getwithref(dst_vp)) {
vnode_put (src_vp);
fp_drop(p, fd2, fp2, 0);
error = ENOENT;
goto outdrop;
- }
-
- /*
+ }
+
+ /*
* Basic asserts; validate they are not the same and that
* both live on the same filesystem.
*/
fp_drop (p, fd2, fp2, 0);
error = EINVAL;
goto outdrop;
- }
+ }
if (dst_vp->v_mount != src_vp->v_mount) {
vnode_put (src_vp);
/* Now we have a legit pair of FDs. Go to work */
/* Now check for write access to the target files */
- if(vnode_authorize(src_vp, NULLVP,
+ if(vnode_authorize(src_vp, NULLVP,
(KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA), &context) != 0) {
vnode_put(src_vp);
vnode_put(dst_vp);
error = EBADF;
goto outdrop;
}
-
- if(vnode_authorize(dst_vp, NULLVP,
+
+ if(vnode_authorize(dst_vp, NULLVP,
(KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA), &context) != 0) {
vnode_put(src_vp);
vnode_put(dst_vp);
error = EBADF;
goto outdrop;
}
-
+
/* Verify that both vps point to files and not directories */
if ( !vnode_isreg(src_vp) || !vnode_isreg(dst_vp)) {
error = EINVAL;
goto outdrop;
}
- /*
+ /*
* The exchangedata syscall handler passes in 0 for the flags to VNOP_EXCHANGE.
* We'll pass in our special bit indicating that the new behavior is expected
*/
-
+
error = VNOP_EXCHANGE(src_vp, dst_vp, FSOPT_EXCHANGE_DATA_ONLY, &context);
-
+
vnode_put (src_vp);
vnode_put (dst_vp);
fp_drop(p, fd2, fp2, 0);
break;
}
-
- /*
+
+ /*
* SPI for making a file compressed.
*/
case F_MAKECOMPRESSED: {
uint32_t gcounter = CAST_DOWN_EXPLICIT(uint32_t, uap->arg);
if (fp->f_type != DTYPE_VNODE) {
- error = EBADF;
+ error = EBADF;
goto out;
}
}
/* invoke ioctl to pass off to FS */
- /* Only go forward if you have write access */
+ /* Only go forward if you have write access */
vfs_context_t ctx = vfs_context_current();
if(vnode_authorize(vp, NULLVP, (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA), ctx) != 0) {
vnode_put(vp);
error = VNOP_IOCTL(vp, uap->cmd, (caddr_t)&gcounter, 0, &context);
vnode_put (vp);
- break;
+ break;
}
-
+
/*
* SPI (private) for indicating to a filesystem that subsequent writes to
* the open FD will written to the Fastflow.
error = VNOP_IOCTL(vp, uap->cmd, ioctl_arg, 0, &context);
(void)vnode_put(vp);
-
+
break;
}
* or other flavors that may be necessary.
*/
case F_SETIOTYPE: {
- caddr_t param_ptr;
+ caddr_t param_ptr;
uint32_t param;
if (uap->arg) {
error = EINVAL;
goto out;
}
-
- /*
- * Validate the different types of flags that can be specified:
+
+ /*
+ * Validate the different types of flags that can be specified:
* all of them are mutually exclusive for now.
*/
switch (param) {
break;
}
-
/*
- * Extract the CodeDirectory of the vnode associated with
- * the file descriptor and copy it back to user space
- */
- case F_GETCODEDIR: {
- struct user_fcodeblobs args;
-
- if (fp->f_type != DTYPE_VNODE) {
- error = EBADF;
- goto out;
- }
-
- vp = (struct vnode *)fp->f_data;
- proc_fdunlock(p);
-
- if ((fp->f_flag & FREAD) == 0) {
- error = EBADF;
- goto outdrop;
- }
-
- if (IS_64BIT_PROCESS(p)) {
- struct user64_fcodeblobs args64;
-
- error = copyin(argp, &args64, sizeof(args64));
- if (error)
- goto outdrop;
-
- args.f_cd_hash = args64.f_cd_hash;
- args.f_hash_size = args64.f_hash_size;
- args.f_cd_buffer = args64.f_cd_buffer;
- args.f_cd_size = args64.f_cd_size;
- args.f_out_size = args64.f_out_size;
- args.f_arch = args64.f_arch;
- } else {
- struct user32_fcodeblobs args32;
-
- error = copyin(argp, &args32, sizeof(args32));
- if (error)
- goto outdrop;
-
- args.f_cd_hash = CAST_USER_ADDR_T(args32.f_cd_hash);
- args.f_hash_size = args32.f_hash_size;
- args.f_cd_buffer = CAST_USER_ADDR_T(args32.f_cd_buffer);
- args.f_cd_size = args32.f_cd_size;
- args.f_out_size = CAST_USER_ADDR_T(args32.f_out_size);
- args.f_arch = args32.f_arch;
- }
-
- if (vp->v_ubcinfo == NULL) {
- error = EINVAL;
- goto outdrop;
- }
-
- struct cs_blob *t_blob = vp->v_ubcinfo->cs_blobs;
-
- /*
- * This call fails if there is no cs_blob corresponding to the
- * vnode, or if there are multiple cs_blobs present, and the caller
- * did not specify which cpu_type they want the cs_blob for
- */
- if (t_blob == NULL) {
- error = ENOENT; /* there is no codesigning blob for this process */
- goto outdrop;
- } else if (args.f_arch == 0 && t_blob->csb_next != NULL) {
- error = ENOENT; /* too many architectures and none specified */
- goto outdrop;
- }
-
- /* If the user specified an architecture, find the right blob */
- if (args.f_arch != 0) {
- while (t_blob) {
- if (t_blob->csb_cpu_type == args.f_arch)
- break;
- t_blob = t_blob->csb_next;
- }
- /* The cpu_type the user requested could not be found */
- if (t_blob == NULL) {
- error = ENOENT;
- goto outdrop;
- }
- }
-
- const CS_SuperBlob *super_blob = (void *)t_blob->csb_mem_kaddr;
- const CS_CodeDirectory *cd = findCodeDirectory(super_blob,
- (char *) super_blob,
- (char *) super_blob + t_blob->csb_mem_size);
- if (cd == NULL) {
- error = ENOENT;
- goto outdrop;
- }
-
- uint64_t buffer_size = ntohl(cd->length);
-
- if (buffer_size > UINT_MAX) {
- error = ERANGE;
- goto outdrop;
- }
-
- error = copyout(&buffer_size, args.f_out_size, sizeof(unsigned int));
- if (error)
- goto outdrop;
-
- if (sizeof(t_blob->csb_sha1) > args.f_hash_size ||
- buffer_size > args.f_cd_size) {
- error = ERANGE;
- goto outdrop;
- }
-
- error = copyout(t_blob->csb_sha1, args.f_cd_hash, sizeof(t_blob->csb_sha1));
- if (error)
- goto outdrop;
- error = copyout(cd, args.f_cd_buffer, buffer_size);
- if (error)
- goto outdrop;
-
- break;
- }
-
- /*
* Set the vnode pointed to by 'fd'
* and tag it as the (potentially future) backing store
* for another filesystem
error = EBADF;
goto out;
}
-
+
vp = (struct vnode *)fp->f_data;
if (vp->v_tag != VT_HFS) {
error = ENOENT;
goto outdrop;
}
-
+
/* only proceed if you have write access */
vfs_context_t ctx = vfs_context_current();
if(vnode_authorize(vp, NULLVP, (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA), ctx) != 0) {
goto outdrop;
}
-
+
/* If arg != 0, set, otherwise unset */
if (uap->arg) {
error = VNOP_IOCTL (vp, uap->cmd, (caddr_t)1, 0, &context);
else {
error = VNOP_IOCTL (vp, uap->cmd, (caddr_t)NULL, 0, &context);
}
-
+
vnode_put(vp);
break;
}
- /*
+ /*
* like F_GETPATH, but special semantics for
* the mobile time machine handler.
*/
}
if ( (error = vnode_getwithref(vp)) == 0 ) {
int backingstore = 0;
-
+
/* Check for error from vn_getpath before moving on */
if ((error = vn_getpath(vp, pathbufp, &pathlen)) == 0) {
if (vp->v_tag == VT_HFS) {
error = copyout((caddr_t)pathbufp, argp, pathlen);
}
if (error == 0) {
- /*
+ /*
* If the copyout was successful, now check to ensure
* that this vnode is not a BACKINGSTORE vnode. mtmd
* wants the path regardless.
goto outdrop;
}
+#if DEBUG || DEVELOPMENT
+ case F_RECYCLE:
+ if (fp->f_type != DTYPE_VNODE) {
+ error = EBADF;
+ goto out;
+ }
+ vp = (struct vnode *)fp->f_data;
+ proc_fdunlock(p);
+
+ vnode_recycle(vp);
+ break;
+#endif
+
default:
/*
* This is an fcntl() that we d not recognize at this level;
error = EINVAL;
goto out;
}
-
+
/* Catch any now-invalid fcntl() selectors */
switch (uap->cmd) {
case F_MARKDEPENDENCY:
if ( (error = vnode_getwithref(vp)) == 0 ) {
#define STK_PARAMS 128
- char stkbuf[STK_PARAMS];
+ char stkbuf[STK_PARAMS] = {0};
unsigned int size;
caddr_t data, memp;
/*
} else {
data = &stkbuf[0];
}
-
+
if (uap->cmd & IOC_IN) {
if (size) {
/* structure */
(void)vnode_put(vp);
/* Copy any output data to user */
- if (error == 0 && (uap->cmd & IOC_OUT) && size)
+ if (error == 0 && (uap->cmd & IOC_OUT) && size)
error = copyout(data, argp, size);
if (memp)
kfree(memp, size);
if ( (fp->f_type == DTYPE_VNODE) && kauth_authorize_fileop_has_listeners() ) {
/*
- * call out to allow 3rd party notification of close.
+ * call out to allow 3rd party notification of close.
* Ignore result of kauth_authorize_fileop call.
*/
if (vnode_getwithref((vnode_t)fp->f_data) == 0) {
u_int fileop_flags = 0;
if ((fp->f_flags & FP_WRITTEN) != 0)
fileop_flags |= KAUTH_FILEOP_CLOSE_MODIFIED;
- kauth_authorize_fileop(fp->f_fglob->fg_cred, KAUTH_FILEOP_CLOSE,
+ kauth_authorize_fileop(fp->f_fglob->fg_cred, KAUTH_FILEOP_CLOSE,
(uintptr_t)fp->f_data, (uintptr_t)fileop_flags);
vnode_put((vnode_t)fp->f_data);
}
}
if (fd < fdp->fd_knlistsize)
- knote_fdclose(p, fd);
+ knote_fdclose(p, fd, FALSE);
- if (fp->f_flags & FP_WAITEVENT)
+ if (fp->f_flags & FP_WAITEVENT)
(void)waitevent_close(p, fp);
fileproc_drain(p, fp);
procfdtbl_reservefd(p, fd);
}
+ if (ENTR_SHOULDTRACE && fp->f_type == DTYPE_SOCKET)
+ KERNEL_ENERGYTRACE(kEnTrActKernSocket, DBG_FUNC_END,
+ fd, 0, (int64_t)VM_KERNEL_ADDRPERM(fp->f_data));
+
error = closef_locked(fp, fp->f_fglob, p);
if ((fp->f_flags & FP_WAITCLOSE) == FP_WAITCLOSE)
wakeup(&fp->f_flags);
proc_fdunlock(p);
- fileproc_free(fp);
+ fileproc_free(fp);
proc_fdlock(p);
* going to let them get the basic stat information.
*/
if (xsecurity == USER_ADDR_NULL) {
- error = vn_stat_noauth((vnode_t)data, sbptr, NULL, isstat64, ctx);
+ error = vn_stat_noauth((vnode_t)data, sbptr, NULL, isstat64, ctx,
+ fp->f_fglob->fg_cred);
} else {
error = vn_stat((vnode_t)data, sbptr, &fsec, isstat64, ctx);
}
source.sb64.st_qspare[1] = 0LL;
if (IS_64BIT_PROCESS(current_proc())) {
- munge_user64_stat64(&source.sb64, &dest.user64_sb64);
+ munge_user64_stat64(&source.sb64, &dest.user64_sb64);
my_size = sizeof(dest.user64_sb64);
sbp = (caddr_t)&dest.user64_sb64;
} else {
- munge_user32_stat64(&source.sb64, &dest.user32_sb64);
+ munge_user32_stat64(&source.sb64, &dest.user32_sb64);
my_size = sizeof(dest.user32_sb64);
sbp = (caddr_t)&dest.user32_sb64;
}
source.sb.st_qspare[0] = 0LL;
source.sb.st_qspare[1] = 0LL;
if (IS_64BIT_PROCESS(current_proc())) {
- munge_user64_stat(&source.sb, &dest.user64_sb);
+ munge_user64_stat(&source.sb, &dest.user64_sb);
my_size = sizeof(dest.user64_sb);
sbp = (caddr_t)&dest.user64_sb;
} else {
- munge_user32_stat(&source.sb, &dest.user32_sb);
+ munge_user32_stat(&source.sb, &dest.user32_sb);
my_size = sizeof(dest.user32_sb);
sbp = (caddr_t)&dest.user32_sb;
}
{
return(fstat1(p, uap->fd, uap->ub, uap->xsecurity, uap->xsecurity_size, 0));
}
-
+
/*
* fstat
* !0 Errno (see fstat1)
*/
int
-fstat(proc_t p, register struct fstat_args *uap, __unused int32_t *retval)
+fstat(proc_t p, struct fstat_args *uap, __unused int32_t *retval)
{
return(fstat1(p, uap->fd, uap->ub, 0, 0, 0));
}
{
return(fstat1(p, uap->fd, uap->ub, uap->xsecurity, uap->xsecurity_size, 1));
}
-
+
/*
* fstat64
* !0 Errno (see fstat1)
*/
int
-fstat64(proc_t p, register struct fstat64_args *uap, __unused int32_t *retval)
+fstat64(proc_t p, struct fstat64_args *uap, __unused int32_t *retval)
{
return(fstat1(p, uap->fd, uap->ub, 0, 0, 1));
}
return (0);
}
+
/*
* fp_lookup
*
*resultfp = fp;
if (!locked)
proc_fdunlock(p);
-
+
return (0);
}
/*
* fp_tryswap
- *
+ *
* Description: Swap the fileproc pointer for a given fd with a new
* fileproc pointer in the per-process open file table of
* the specified process. The fdlock must be held at entry.
(fp->f_flags & ~FP_TYPEMASK);
nfp->f_iocount = fp->f_iocount;
nfp->f_fglob = fp->f_fglob;
- nfp->f_waddr = fp->f_waddr;
+ nfp->f_wset = fp->f_wset;
p->p_fd->fd_ofiles[fd] = nfp;
(void) fp_drop(p, fd, nfp, 1);
proc_fdlock_spin(p);
fp->f_flags |= FP_WRITTEN;
-
+
error = fp_drop(p, fd, fp, 1);
proc_fdunlock(p);
-
+
return (error);
}
proc_fdlock_spin(p);
fp->f_flags |= FP_WAITEVENT;
-
+
error = fp_drop(p, fd, fp, 1);
proc_fdunlock(p);
-
+
return (error);
}
proc_fdunlock(p);
if (needwakeup)
wakeup(&p->p_fpdrainwait);
-
+
return (0);
}
proc_t p = current_proc();
struct fileproc *fp;
int error;
-
+
proc_fdlock_spin(p);
if ( (error = fp_lookup(p, fd, &fp, 1)) ) {
proc_fdunlock(p);
struct fileproc *fp;
vnode_t vp;
int error;
-
+
proc_fdlock_spin(p);
if ( (error = fp_lookup(p, fd, &fp, 1)) ) {
proc_fdunlock(p);
return(EINVAL);
}
vp = (struct vnode *)fp->f_data;
- if (vpp != NULL)
+ if (vpp != NULL)
*vpp = vp;
- if ((vidp != NULL) && (vp != NULLVP))
+ if ((vidp != NULL) && (vp != NULLVP))
*vidp = (uint32_t)vp->v_id;
proc_fdunlock(p);
proc_t p = current_proc();
struct fileproc *fp;
int error;
-
+
proc_fdlock_spin(p);
if ( (error = fp_lookup(p, fd, &fp, 1)) ) {
proc_fdunlock(p);
proc_t p = current_proc();
struct fileproc *fp;
int error;
-
+
proc_fdlock_spin(p);
if ( (error = fp_lookup(p, fd, &fp, 1)) ) {
proc_fdunlock(p);
*
* Use of this function is discouraged.
*/
-int
+int
file_drop(int fd)
{
struct fileproc *fp;
|| (fp && mac_file_check_inherit(proc_ucred(p), fp->f_fglob))
#endif
) {
- if (i < fdp->fd_knlistsize)
- knote_fdclose(p, i);
+ if (i < fdp->fd_knlistsize)
+ knote_fdclose(p, i, TRUE);
procfdtbl_clearfd(p, i);
if (i == fdp->fd_lastfile && i > 0)
fdp->fd_lastfile--;
* allowing the table to shrink.
*/
i = newfdp->fd_nfiles;
- while (i > 2 * NDEXTENT && i > newfdp->fd_lastfile * 2)
+ while (i > 1 + 2 * NDEXTENT && i > 1 + newfdp->fd_lastfile * 2)
i /= 2;
}
proc_fdunlock(p);
for (i = newfdp->fd_lastfile + 1; --i >= 0; fpp++, flags++)
if ((ofp = *fpp) != NULL &&
+ 0 == (ofp->f_fglob->fg_lflags & FG_CONFINED) &&
0 == (*flags & (UF_FORKCLOSE|UF_RESERVED))) {
#if DEBUG
if (FILEPROC_TYPE(ofp) != FTYPE_SIMPLE)
panic("filedesc0");
if (fdp->fd_nfiles > 0 && fdp->fd_ofiles) {
- for (i = fdp->fd_lastfile; i >= 0; i--) {
+ for (i = fdp->fd_lastfile; i >= 0; i--) {
+
+ /* May still have knotes for fd without open file */
+ if (i < fdp->fd_knlistsize)
+ knote_fdclose(p, i, TRUE);
+
if ((fp = fdp->fd_ofiles[i]) != NULL) {
-
+
if (fdp->fd_ofileflags[i] & UF_RESERVED)
panic("fdfree: found fp with UF_RESERVED");
procfdtbl_reservefd(p, i);
- if (i < fdp->fd_knlistsize)
- knote_fdclose(p, i);
- if (fp->f_flags & FP_WAITEVENT)
+ if (fp->f_flags & FP_WAITEVENT)
(void)waitevent_close(p, fp);
(void) closef_locked(fp, fp->f_fglob, p);
fileproc_free(fp);
FREE_ZONE(fdp->fd_ofiles, fdp->fd_nfiles * OFILESIZE, M_OFILETABL);
fdp->fd_ofiles = NULL;
fdp->fd_nfiles = 0;
- }
+ }
proc_fdunlock(p);
-
+
if (fdp->fd_cdir)
vnode_rele(fdp->fd_cdir);
if (fdp->fd_rdir)
if (p)
proc_fdunlock(p);
- /* Since we ensure that fg->fg_ops is always initialized,
+ /* Since we ensure that fg->fg_ops is always initialized,
* it is safe to invoke fo_close on the fg */
error = fo_close(fg, &context);
fg_free(fg);
-
+
if (p)
proc_fdlock(p);
(*fp->f_fglob->fg_ops->fo_drain)(fp, &context);
}
if ((fp->f_flags & FP_INSELECT) == FP_INSELECT) {
- if (wait_queue_wakeup_all((wait_queue_t)fp->f_waddr, NULL, THREAD_INTERRUPTED) == KERN_INVALID_ARGUMENT)
- panic("bad wait queue for wait_queue_wakeup_all %p", fp->f_waddr);
- }
+ if (waitq_wakeup64_all((struct waitq *)fp->f_wset, NO_EVENT64,
+ THREAD_INTERRUPTED, WAITQ_ALL_PRIORITIES) == KERN_INVALID_ARGUMENT)
+ panic("bad wait queue for waitq_wakeup64_all %p (fp:%p)", fp->f_wset, fp);
+ }
if ((fp->f_flags & FP_SELCONFLICT) == FP_SELCONFLICT) {
- if (wait_queue_wakeup_all(&select_conflict_queue, NULL, THREAD_INTERRUPTED) == KERN_INVALID_ARGUMENT)
+ if (waitq_wakeup64_all(&select_conflict_queue, NO_EVENT64,
+ THREAD_INTERRUPTED, WAITQ_ALL_PRIORITIES) == KERN_INVALID_ARGUMENT)
panic("bad select_conflict_queue");
}
p->p_fpdrainwait = 1;
* attempted
* uap->how (Un)Lock bits, including type
* retval Pointer to the call return area
- *
+ *
* Returns: 0 Success
* fp_getfvp:EBADF Bad file descriptor
* fp_getfvp:ENOTSUP fd does not refer to a vnode
if (error)
goto out;
#endif
- fp->f_flag |= FHASLOCK;
- if (how & LOCK_NB) {
- error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob, F_SETLK, &lf, F_FLOCK, ctx, NULL);
- goto out;
- }
- error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob, F_SETLK, &lf, F_FLOCK|F_WAIT, ctx, NULL);
+ error = VNOP_ADVLOCK(vp, (caddr_t)fp->f_fglob, F_SETLK, &lf,
+ (how & LOCK_NB ? F_FLOCK : F_FLOCK | F_WAIT),
+ ctx, NULL);
+ if (!error)
+ fp->f_flag |= FHASLOCK;
out:
(void)vnode_put(vp);
out1:
* EAGAIN Resource shortage.
*
* Implicit returns:
- * On success, name of send right is stored at user-specified address.
+ * On success, name of send right is stored at user-specified address.
*/
int
fileport_makeport(proc_t p, struct fileport_makeport_args *uap,
ipc_port_t fileport;
mach_port_name_t name = MACH_PORT_NULL;
- err = fp_lookup(p, fd, &fp, 0);
+ proc_fdlock(p);
+ err = fp_lookup(p, fd, &fp, 1);
if (err != 0) {
- goto out;
+ goto out_unlock;
}
- if (!filetype_issendable(fp->f_type)) {
+ if (!file_issendable(p, fp)) {
err = EINVAL;
- goto out;
+ goto out_unlock;
}
if (FP_ISGUARDED(fp, GUARD_FILEPORT)) {
- proc_fdlock(p);
err = fp_guard_exception(p, fd, fp, kGUARD_EXC_FILEPORT);
- proc_fdunlock(p);
- goto out;
+ goto out_unlock;
}
/* Dropped when port is deallocated */
fg = fp->f_fglob;
fg_ref(fp);
+ proc_fdunlock(p);
+
/* Allocate and initialize a port */
fileport = fileport_alloc(fg);
if (fileport == IPC_PORT_NULL) {
fg_drop(fp);
goto out;
}
-
+
/* Add an entry. Deallocates port on failure. */
name = ipc_port_copyout_send(fileport, get_task_ipcspace(p->task));
if (!MACH_PORT_VALID(name)) {
err = EINVAL;
goto out;
- }
-
+ }
+
err = copyout(&name, user_portaddr, sizeof(mach_port_name_t));
if (err != 0) {
goto out;
return 0;
+out_unlock:
+ proc_fdunlock(p);
out:
if (MACH_PORT_VALID(name)) {
/* Don't care if another thread races us to deallocate the entry */
out:
if ((fp != NULL) && (0 != err)) {
fileproc_free(fp);
- }
+ }
if (IPC_PORT_NULL != port) {
ipc_port_release_send(port);
switch (error) {
case ENODEV:
if (FP_ISGUARDED(wfp, GUARD_DUP)) {
- int err = fp_guard_exception(p,
- dfd, wfp, kGUARD_EXC_DUP);
proc_fdunlock(p);
- return (err);
+ return (EPERM);
}
/*
#if SOCKETS
/*
- * fg_insertuipc
+ * fg_insertuipc_mark
*
- * Description: Insert fileglob onto message queue
+ * Description: Mark fileglob for insertion onto message queue if needed
+ * Also takes fileglob reference
*
- * Parameters: fg Fileglob pointer to insert
+ * Parameters: fg Fileglob pointer to insert
*
- * Returns: void
+ * Returns: true, if the fileglob needs to be inserted onto msg queue
*
* Locks: Takes and drops fg_lock, potentially many times
*/
-void
-fg_insertuipc(struct fileglob * fg)
+boolean_t
+fg_insertuipc_mark(struct fileglob * fg)
{
- int insertque = 0;
+ boolean_t insert = FALSE;
lck_mtx_lock_spin(&fg->fg_lock);
-
while (fg->fg_lflags & FG_RMMSGQ) {
lck_mtx_convert_spin(&fg->fg_lock);
fg->fg_msgcount++;
if (fg->fg_msgcount == 1) {
fg->fg_lflags |= FG_INSMSGQ;
- insertque=1;
+ insert = TRUE;
}
lck_mtx_unlock(&fg->fg_lock);
+ return (insert);
+}
- if (insertque) {
+/*
+ * fg_insertuipc
+ *
+ * Description: Insert marked fileglob onto message queue
+ *
+ * Parameters: fg Fileglob pointer to insert
+ *
+ * Returns: void
+ *
+ * Locks: Takes and drops fg_lock & uipc_lock
+ * DO NOT call this function with proc_fdlock held as unp_gc()
+ * can potentially try to acquire proc_fdlock, which can result
+ * in a deadlock if this function is in unp_gc_wait().
+ */
+void
+fg_insertuipc(struct fileglob * fg)
+{
+ if (fg->fg_lflags & FG_INSMSGQ) {
lck_mtx_lock_spin(uipc_lock);
unp_gc_wait();
LIST_INSERT_HEAD(&fmsghead, fg, f_msglist);
}
lck_mtx_unlock(&fg->fg_lock);
}
-
}
-
/*
- * fg_removeuipc
+ * fg_removeuipc_mark
*
- * Description: Remove fileglob from message queue
+ * Description: Mark the fileglob for removal from message queue if needed
+ * Also releases fileglob message queue reference
*
- * Parameters: fg Fileglob pointer to remove
+ * Parameters: fg Fileglob pointer to remove
*
- * Returns: void
+ * Returns: true, if the fileglob needs to be removed from msg queue
*
* Locks: Takes and drops fg_lock, potentially many times
*/
-void
-fg_removeuipc(struct fileglob * fg)
+boolean_t
+fg_removeuipc_mark(struct fileglob * fg)
{
- int removeque = 0;
+ boolean_t remove = FALSE;
lck_mtx_lock_spin(&fg->fg_lock);
while (fg->fg_lflags & FG_INSMSGQ) {
fg->fg_msgcount--;
if (fg->fg_msgcount == 0) {
fg->fg_lflags |= FG_RMMSGQ;
- removeque=1;
+ remove = TRUE;
}
lck_mtx_unlock(&fg->fg_lock);
+ return (remove);
+}
- if (removeque) {
+/*
+ * fg_removeuipc
+ *
+ * Description: Remove marked fileglob from message queue
+ *
+ * Parameters: fg Fileglob pointer to remove
+ *
+ * Returns: void
+ *
+ * Locks: Takes and drops fg_lock & uipc_lock
+ * DO NOT call this function with proc_fdlock held as unp_gc()
+ * can potentially try to acquire proc_fdlock, which can result
+ * in a deadlock if this function is in unp_gc_wait().
+ */
+void
+fg_removeuipc(struct fileglob * fg)
+{
+ if (fg->fg_lflags & FG_RMMSGQ) {
lck_mtx_lock_spin(uipc_lock);
unp_gc_wait();
LIST_REMOVE(fg, f_msglist);
* function, it will need to revalidate/reacquire any cached
* protected data obtained prior to the call.
*/
-int
+int
fo_ioctl(struct fileproc *fp, u_long com, caddr_t data, vfs_context_t ctx)
{
int error;
error = (*fp->f_ops->fo_ioctl)(fp, com, data, ctx);
proc_fdlock(vfs_context_proc(ctx));
return(error);
-}
+}
/*
*/
int
fo_select(struct fileproc *fp, int which, void *wql, vfs_context_t ctx)
-{
+{
return((*fp->f_ops->fo_select)(fp, which, wql, ctx));
}
*/
int
fo_close(struct fileglob *fg, vfs_context_t ctx)
-{
+{
return((*fg->fg_ops->fo_close)(fg, ctx));
}
* kn pointer to knote to filter on
* ctx VFS context for operation
*
- * Returns: 0 Success
- * !0 Errno from kqueue filter
+ * Returns: (kn->kn_flags & EV_ERROR) error in kn->kn_data
+ * 0 Filter is not active
+ * !0 Filter is active
*/
int
fo_kqfilter(struct fileproc *fp, struct knote *kn, vfs_context_t ctx)
* process is opt-in by file type.
*/
boolean_t
-filetype_issendable(file_type_t fdtype)
+file_issendable(proc_t p, struct fileproc *fp)
{
- switch (fdtype) {
- case DTYPE_VNODE:
- case DTYPE_SOCKET:
- case DTYPE_PIPE:
- case DTYPE_PSXSHM:
- return TRUE;
- default:
- /* DTYPE_KQUEUE, DTYPE_FSEVENTS, DTYPE_PSXSEM */
- return FALSE;
+ proc_fdlock_assert(p, LCK_MTX_ASSERT_OWNED);
+
+ switch (fp->f_type) {
+ case DTYPE_VNODE:
+ case DTYPE_SOCKET:
+ case DTYPE_PIPE:
+ case DTYPE_PSXSHM:
+ return (0 == (fp->f_fglob->fg_lflags & FG_CONFINED));
+ default:
+ /* DTYPE_KQUEUE, DTYPE_FSEVENTS, DTYPE_PSXSEM */
+ return FALSE;
}
}