/*
- * Copyright (c) 2000-2015 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 <mach/mach_port.h>
#include <stdbool.h>
-#include <hfs/hfs.h>
+#if CONFIG_MACF
+#include <security/mac_framework.h>
+#endif
kern_return_t ipc_object_copyin(ipc_space_t, mach_port_name_t,
mach_msg_type_name_t, ipc_port_t *);
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);
while ((nfd = fdp->fd_lastfile) > 0 &&
fdp->fd_ofiles[nfd] == NULL &&
!(fdp->fd_ofileflags[nfd] & UF_RESERVED))
+ /* JMM - What about files with lingering EV_VANISHED knotes? */
fdp->fd_lastfile--;
}
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))
case F_GETLK:
case F_OFD_GETLK:
+#if CONFIG_EMBEDDED
+ case F_GETLKPID:
+ case F_OFD_GETLKPID:
+#endif
if (fp->f_type != DTYPE_VNODE) {
error = EBADF;
goto out;
/* 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);
}
} else {
a_size = devBlockSize;
}
-
+
error = VNOP_BLOCKMAP(vp, offset, a_size, &bn, &run, NULL, 0, &context);
(void)vnode_put(vp);
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;
{
/* If this is for dyld_sim revalidate the blob */
if (uap->cmd == F_ADDFILESIGS_FOR_DYLD_SIM) {
- error = ubc_cs_blob_revalidate(vp, blob, blob_add_flags);
+ error = ubc_cs_blob_revalidate(vp, blob, NULL, blob_add_flags);
+ if (error) {
+ vnode_put(vp);
+ goto outdrop;
+ }
}
} else {
memset((void *)(kernel_blob_addr + (kernel_blob_size - resid)), 0x0, resid);
}
}
-
+
if (error) {
ubc_cs_blob_deallocate(kernel_blob_addr,
- kernel_blob_size);
+ 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_addr,
kernel_blob_size,
+ NULL,
blob_add_flags,
&blob);
+
+ /* ubc_blob_add() has consumed "kernel_blob_addr" if it is zeroed */
if (error) {
- ubc_cs_blob_deallocate(kernel_blob_addr,
- kernel_blob_size);
+ if (kernel_blob_addr) {
+ ubc_cs_blob_deallocate(kernel_blob_addr,
+ kernel_blob_size);
+ }
+ vnode_put(vp);
+ goto outdrop;
} else {
- /* ubc_blob_add() has consumed "kernel_blob_addr" */
#if CHECK_CS_VALIDATION_BITMAP
ubc_cs_validation_bitmap_allocate( vp );
#endif
(void) vnode_put(vp);
break;
}
+ case F_GETCODEDIR:
case F_FINDSIGS: {
error = ENOTSUP;
goto out;
}
+ case F_CHECK_LV: {
+ struct fileglob *fg;
+ fchecklv_t lv;
+
+ if (fp->f_type != DTYPE_VNODE) {
+ error = EBADF;
+ goto out;
+ }
+ fg = fp->f_fglob;
+ proc_fdunlock(p);
+
+ 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 = (void *)(uintptr_t)lv32.lv_error_message;
+ lv.lv_error_message_size = lv32.lv_error_message;
+ }
+ if (error)
+ goto outdrop;
+
+#if CONFIG_MACF
+ error = mac_file_check_library_validation(p, fg, lv.lv_file_start,
+ (user_long_t)lv.lv_error_message, lv.lv_error_message_size);
+#endif
+
+ 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,
- (const char *) super_blob,
- (const 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_cdhash) > args.f_hash_size ||
- buffer_size > args.f_cd_size) {
- error = ERANGE;
- goto outdrop;
- }
-
- error = copyout(t_blob->csb_cdhash, args.f_cd_hash, sizeof(t_blob->csb_cdhash));
- 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.
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);
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.
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;
}
+
/*
* fdexec
*
* Returns: void
*
* Locks: This function internally takes and drops proc_fdlock()
+ * But assumes tables don't grow/change while unlocked.
*
*/
void
-fdexec(proc_t p, short flags)
+fdexec(proc_t p, short flags, int self_exec)
{
struct filedesc *fdp = p->p_fd;
int i;
boolean_t cloexec_default = (flags & POSIX_SPAWN_CLOEXEC_DEFAULT) != 0;
+ thread_t self = current_thread();
+ struct uthread *ut = get_bsdthread_info(self);
+ struct kqueue *dealloc_kq = NULL;
+
+ /*
+ * If the current thread is bound as a workq/workloop
+ * servicing thread, we need to unbind it first.
+ */
+ if (ut->uu_kqueue_bound && self_exec) {
+ kevent_qos_internal_unbind(p, 0, self,
+ ut->uu_kqueue_flags);
+ }
proc_fdlock(p);
+
+ /*
+ * Deallocate the knotes for this process
+ * and mark the tables non-existent so
+ * subsequent kqueue closes go faster.
+ */
+ knotes_dealloc(p);
+ assert(fdp->fd_knlistsize == -1);
+ assert(fdp->fd_knhashmask == 0);
+
for (i = fdp->fd_lastfile; i >= 0; i--) {
struct fileproc *fp = fdp->fd_ofiles[i];
|| (fp && mac_file_check_inherit(proc_ucred(p), fp->f_fglob))
#endif
) {
- if (i < fdp->fd_knlistsize)
- knote_fdclose(p, i);
procfdtbl_clearfd(p, i);
if (i == fdp->fd_lastfile && i > 0)
fdp->fd_lastfile--;
fileproc_free(fp);
}
}
+
+ /* release the per-process workq kq */
+ if (fdp->fd_wqkqueue) {
+ dealloc_kq = fdp->fd_wqkqueue;
+ fdp->fd_wqkqueue = NULL;
+ }
+
proc_fdunlock(p);
+
+ /* Anything to free? */
+ if (dealloc_kq)
+ kqueue_dealloc(dealloc_kq);
}
if (*fpp == NULL && i == newfdp->fd_lastfile && i > 0)
newfdp->fd_lastfile--;
}
- newfdp->fd_knlist = NULL;
- newfdp->fd_knlistsize = -1;
- newfdp->fd_knhash = NULL;
- newfdp->fd_knhashmask = 0;
}
fpp = newfdp->fd_ofiles;
flags = newfdp->fd_ofileflags;
}
proc_fdunlock(p);
+
+ /*
+ * Initialize knote and kqueue tracking structs
+ */
+ newfdp->fd_knlist = NULL;
+ newfdp->fd_knlistsize = -1;
+ newfdp->fd_knhash = NULL;
+ newfdp->fd_knhashmask = 0;
+ newfdp->fd_kqhash = NULL;
+ newfdp->fd_kqhashmask = 0;
+ newfdp->fd_wqkqueue = NULL;
+ lck_mtx_init(&newfdp->fd_kqhashlock, proc_kqhashlock_grp, proc_lck_attr);
+ lck_mtx_init(&newfdp->fd_knhashlock, proc_knhashlock_grp, proc_lck_attr);
+
return (newfdp);
}
{
struct filedesc *fdp;
struct fileproc *fp;
+ struct kqueue *dealloc_kq = NULL;
int i;
proc_fdlock(p);
if (&filedesc0 == fdp)
panic("filedesc0");
+ /*
+ * deallocate all the knotes up front and claim empty
+ * tables to make any subsequent kqueue closes faster.
+ */
+ knotes_dealloc(p);
+ assert(fdp->fd_knlistsize == -1);
+ assert(fdp->fd_knhashmask == 0);
+
+ /* close file descriptors */
if (fdp->fd_nfiles > 0 && fdp->fd_ofiles) {
- for (i = fdp->fd_lastfile; i >= 0; i--) {
+ for (i = fdp->fd_lastfile; i >= 0; i--) {
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;
- }
+ }
+
+ if (fdp->fd_wqkqueue) {
+ dealloc_kq = fdp->fd_wqkqueue;
+ fdp->fd_wqkqueue = NULL;
+ }
proc_fdunlock(p);
-
+
+ if (dealloc_kq)
+ kqueue_dealloc(dealloc_kq);
+
if (fdp->fd_cdir)
- vnode_rele(fdp->fd_cdir);
+ vnode_rele(fdp->fd_cdir);
if (fdp->fd_rdir)
vnode_rele(fdp->fd_rdir);
p->p_fd = NULL;
proc_fdunlock(p);
- if (fdp->fd_knlist)
- FREE(fdp->fd_knlist, M_KQUEUE);
- if (fdp->fd_knhash)
- FREE(fdp->fd_knhash, M_KQUEUE);
+ if (fdp->fd_kqhash) {
+ for (uint32_t j = 0; j <= fdp->fd_kqhashmask; j++)
+ assert(SLIST_EMPTY(&fdp->fd_kqhash[j]));
+ FREE(fdp->fd_kqhash, M_KQUEUE);
+ }
+
+ lck_mtx_destroy(&fdp->fd_kqhashlock, proc_kqhashlock_grp);
+ lck_mtx_destroy(&fdp->fd_knhashlock, proc_knhashlock_grp);
FREE_ZONE(fdp, sizeof(*fdp), M_FILEDESC);
}
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);
* 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,
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;
err = fdalloc(p, 0, &fd);
if (err != 0) {
proc_fdunlock(p);
+ fg_drop(fp);
goto out;
}
*fdflags(p, fd) |= UF_EXCLOSE;
out:
if ((fp != NULL) && (0 != err)) {
fileproc_free(fp);
- }
+ }
if (IPC_PORT_NULL != port) {
ipc_port_release_send(port);
* 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)
+fo_kqfilter(struct fileproc *fp, struct knote *kn,
+ struct kevent_internal_s *kev, vfs_context_t ctx)
{
- return ((*fp->f_ops->fo_kqfilter)(fp, kn, ctx));
+ return ((*fp->f_ops->fo_kqfilter)(fp, kn, kev, ctx));
}
/*
* process is opt-in by file type.
*/
boolean_t
-file_issendable(proc_t p, struct fileproc *fp)
+file_issendable(proc_t p, struct fileproc *fp)
{
proc_fdlock_assert(p, LCK_MTX_ASSERT_OWNED);
case DTYPE_SOCKET:
case DTYPE_PIPE:
case DTYPE_PSXSHM:
+ case DTYPE_NETPOLICY:
return (0 == (fp->f_fglob->fg_lflags & FG_CONFINED));
default:
/* DTYPE_KQUEUE, DTYPE_FSEVENTS, DTYPE_PSXSEM */