+ break;
+ }
+
+ /*
+ * SPI (private) for opening a file starting from a dir fd
+ */
+ case F_OPENFROM: {
+ struct user_fopenfrom fopen;
+ struct vnode_attr va;
+ struct nameidata nd;
+ int cmode;
+
+ /* Check if this isn't a valid file descriptor */
+ if ((fp->f_type != DTYPE_VNODE) ||
+ (fp->f_flag & FREAD) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ vp = (struct vnode *)fp->f_data;
+ proc_fdunlock(p);
+
+ if (vnode_getwithref(vp)) {
+ error = ENOENT;
+ goto outdrop;
+ }
+
+ /* Only valid for directories */
+ if (vp->v_type != VDIR) {
+ vnode_put(vp);
+ error = ENOTDIR;
+ goto outdrop;
+ }
+
+ /* Get flags, mode and pathname arguments. */
+ if (IS_64BIT_PROCESS(p)) {
+ error = copyin(argp, &fopen, sizeof(fopen));
+ } else {
+ struct user32_fopenfrom fopen32;
+
+ error = copyin(argp, &fopen32, sizeof(fopen32));
+ fopen.o_flags = fopen32.o_flags;
+ fopen.o_mode = fopen32.o_mode;
+ fopen.o_pathname = CAST_USER_ADDR_T(fopen32.o_pathname);
+ }
+ if (error) {
+ vnode_put(vp);
+ goto outdrop;
+ }
+ AUDIT_ARG(fflags, fopen.o_flags);
+ AUDIT_ARG(mode, fopen.o_mode);
+ VATTR_INIT(&va);
+ /* Mask off all but regular access permissions */
+ cmode = ((fopen.o_mode &~ fdp->fd_cmask) & ALLPERMS) & ~S_ISTXT;
+ VATTR_SET(&va, va_mode, cmode & ACCESSPERMS);
+
+ /* Start the lookup relative to the file descriptor's vnode. */
+ NDINIT(&nd, LOOKUP, OP_OPEN, USEDVP | FOLLOW | AUDITVNPATH1, UIO_USERSPACE,
+ fopen.o_pathname, &context);
+ nd.ni_dvp = vp;
+
+ error = open1(&context, &nd, fopen.o_flags, &va, retval);
+
+ vnode_put(vp);
+ break;
+ }
+ /*
+ * SPI (private) for unlinking a file starting from a dir fd
+ */
+ case F_UNLINKFROM: {
+ struct nameidata nd;
+ user_addr_t pathname;
+
+ /* Check if this isn't a valid file descriptor */
+ if ((fp->f_type != DTYPE_VNODE) ||
+ (fp->f_flag & FREAD) == 0) {
+ error = EBADF;
+ goto out;
+ }
+ vp = (struct vnode *)fp->f_data;
+ proc_fdunlock(p);
+
+ if (vnode_getwithref(vp)) {
+ error = ENOENT;
+ goto outdrop;
+ }
+
+ /* Only valid for directories */
+ if (vp->v_type != VDIR) {
+ vnode_put(vp);
+ error = ENOTDIR;
+ goto outdrop;
+ }
+
+ /* Get flags, mode and pathname arguments. */
+ if (IS_64BIT_PROCESS(p)) {
+ pathname = (user_addr_t)argp;
+ } else {
+ pathname = CAST_USER_ADDR_T(argp);
+ }
+
+ /* Start the lookup relative to the file descriptor's vnode. */
+ NDINIT(&nd, DELETE, OP_UNLINK, USEDVP | AUDITVNPATH1, UIO_USERSPACE,
+ pathname, &context);
+ nd.ni_dvp = vp;
+
+ error = unlink1(&context, &nd, 0);
+
+ vnode_put(vp);
+ break;
+
+ }
+
+ case F_ADDSIGS:
+ case F_ADDFILESIGS:
+ {
+ struct user_fsignatures fs;
+ kern_return_t kr;
+ vm_offset_t kernel_blob_addr;
+ vm_size_t kernel_blob_size;
+
+ if (fp->f_type != DTYPE_VNODE) {
+ error = EBADF;
+ goto out;
+ }
+ vp = (struct vnode *)fp->f_data;
+ proc_fdunlock(p);
+ error = vnode_getwithref(vp);
+ if (error)
+ goto outdrop;
+
+ if (IS_64BIT_PROCESS(p)) {
+ error = copyin(argp, &fs, sizeof (fs));
+ } else {
+ struct user32_fsignatures fs32;
+
+ error = copyin(argp, &fs32, sizeof (fs32));
+ fs.fs_file_start = fs32.fs_file_start;
+ fs.fs_blob_start = CAST_USER_ADDR_T(fs32.fs_blob_start);
+ fs.fs_blob_size = fs32.fs_blob_size;
+ }
+
+ if (error) {
+ vnode_put(vp);
+ goto outdrop;
+ }
+
+ if(ubc_cs_blob_get(vp, CPU_TYPE_ANY, fs.fs_file_start))
+ {
+ /*
+ if(cs_debug)
+ printf("CODE SIGNING: resident blob offered for: %s\n", vp->v_name);
+ */
+ vnode_put(vp);
+ goto outdrop;
+ }
+
+#define CS_MAX_BLOB_SIZE (1ULL * 1024 * 1024) /* XXX ? */
+ 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;
+ }
+
+ if(uap->cmd == F_ADDSIGS) {
+ error = copyin(fs.fs_blob_start,
+ (void *) kernel_blob_addr,
+ kernel_blob_size);
+ } else /* F_ADDFILESIGS */ {
+ 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(),
+ 0,
+ p);
+ }
+
+ 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);
+ if (error) {
+ ubc_cs_blob_deallocate(kernel_blob_addr,
+ kernel_blob_size);
+ } 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_MARKDEPENDENCY: {
+ struct vnode_attr va;
+ vfs_context_t ctx = vfs_context_current();
+ kauth_cred_t cred;
+
+ if ((current_proc()->p_flag & P_DEPENDENCY_CAPABLE) == 0) {
+ error = EPERM;
+ goto out;
+ }
+
+ 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;
+ }
+
+ if (!vnode_isvroot(vp)) {
+ error = EINVAL;
+ vnode_put(vp);
+ goto outdrop;
+ }
+
+ // get the owner of the root dir
+ VATTR_INIT(&va);
+ VATTR_WANTED(&va, va_uid);
+ if (vnode_getattr(vp, &va, ctx) != 0) {
+ error = EINVAL;
+ vnode_put(vp);
+ goto outdrop;
+ }
+
+ // and last, check that the caller is the super user or
+ // the owner of the mount point
+ cred = vfs_context_ucred(ctx);
+ if (!is_suser() && va.va_uid != kauth_cred_getuid(cred)) {
+ error = EACCES;
+ vnode_put(vp);
+ goto outdrop;
+ }
+
+ // if all those checks pass then we can mark the dependency
+ vfs_markdependency(vp->v_mount);
+ error = 0;
+
+ vnode_put(vp);
+
+ break;
+ }
+
+#ifdef CONFIG_PROTECT
+ case F_GETPROTECTIONCLASS: {
+ int class = 0;
+
+ 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;
+ }
+
+ error = cp_vnode_getclass (vp, &class);
+ if (error == 0) {
+ *retval = class;
+ }
+
+ 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) {
+ vnode_put(vp);
+ error = EBADF;
+ goto outdrop;
+ }
+ error = cp_vnode_setclass (vp, tmp);
+ vnode_put(vp);
+ break;
+ }
+#endif /* CONFIG_PROTECT */
+
+ case F_MOVEDATAEXTENTS: {
+ struct fileproc *fp2 = NULL;
+ struct vnode *src_vp = NULLVP;
+ 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);
+
+ if (fp->f_type != DTYPE_VNODE) {
+ error = EBADF;
+ goto out;
+ }
+ vp = src_vp = (struct vnode *)fp->f_data;
+
+ /* For now, special case HFS+ only, since this is SPI. */
+ if (src_vp->v_tag != VT_HFS) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /* We're still holding the proc FD lock */
+ if ( (error = fp_lookup(p, fd2, &fp2, 1)) ) {
+ error = EBADF;
+ goto out;
+ }
+ if (fp2->f_type != DTYPE_VNODE) {
+ fp_drop(p, fd2, fp2, 1);
+ error = EBADF;
+ goto out;
+ }
+ dst_vp = (struct vnode *)fp2->f_data;
+
+ /* For now, special case HFS+ only, since this is SPI. */
+ if (dst_vp->v_tag != VT_HFS) {
+ fp_drop(p, fd2, fp2, 1);
+ error = EINVAL;
+ goto out;
+ }
+
+#if CONFIG_MACF
+ /* Re-do MAC checks against the new FD, pass in a fake argument */
+ error = mac_file_check_fcntl(proc_ucred(p), fp2->f_fglob, uap->cmd, 0);
+ if (error) {
+ fp_drop(p, fd2, fp2, 1);
+ goto out;
+ }
+#endif
+ /* Audit the 2nd FD */
+ AUDIT_ARG(fd, fd2);
+
+ proc_fdunlock(p);
+
+ /* Proc lock dropped; now we have a legit pair of FDs. Go to work */
+
+ if (vnode_getwithref(src_vp)) {
+ 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.
+ */
+
+ if (dst_vp == src_vp) {
+ vnode_put (src_vp);
+ vnode_put (dst_vp);
+ fp_drop (p, fd2, fp2, 0);
+ error = EINVAL;
+ goto outdrop;
+ }
+
+ if (dst_vp->v_mount != src_vp->v_mount) {
+ vnode_put (src_vp);
+ vnode_put (dst_vp);
+ fp_drop (p, fd2, fp2, 0);
+ error = EXDEV;
+ goto outdrop;
+ }
+
+ /* Now check for write access to the target files */
+ if(vnode_authorize(src_vp, NULLVP,
+ (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA), &context) != 0) {
+ vnode_put(src_vp);
+ vnode_put(dst_vp);
+ fp_drop(p, fd2, fp2, 0);
+ error = EBADF;
+ goto outdrop;
+ }
+
+ if(vnode_authorize(dst_vp, NULLVP,
+ (KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA), &context) != 0) {
+ vnode_put(src_vp);
+ vnode_put(dst_vp);
+ fp_drop(p, fd2, fp2, 0);
+ error = EBADF;
+ goto outdrop;
+ }
+
+ /* Verify that both vps point to files and not directories */
+ if (!vnode_isreg(src_vp) || !vnode_isreg(dst_vp)) {
+ vnode_put(src_vp);
+ vnode_put(dst_vp);
+ fp_drop(p, fd2, fp2, 0);
+ 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;
+ }
+
+ /*
+ * Set the vnode pointed to by 'fd'
+ * and tag it as the (potentially future) backing store
+ * for another filesystem
+ */
+ case F_SETBACKINGSTORE: {
+ if (fp->f_type != DTYPE_VNODE) {
+ error = EBADF;
+ goto out;
+ }
+ vp = (struct vnode *)fp->f_data;
+
+ if (vp->v_tag != VT_HFS) {
+ error = EINVAL;
+ goto out;
+
+ }
+ proc_fdunlock(p);
+
+ if (vnode_getwithref(vp)) {
+ 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) {
+ vnode_put(vp);
+ error = EBADF;
+ goto outdrop;
+ }
+
+
+ /* If arg != 0, set, otherwise unset */
+ if (uap->arg) {
+ error = hfs_set_backingstore (vp, 1);
+ }
+ else {
+ error = hfs_set_backingstore (vp, 0);
+ }
+ /* Success. explicitly set error to 0. */
+ error = 0;
+
+ vnode_put(vp);
+ break;
+ }
+
+ /*
+ * like F_GETPATH, but special semantics for
+ * the mobile time machine handler.
+ */
+ case F_GETPATH_MTMINFO: {
+ char *pathbufp;
+ int pathlen;
+
+ if (fp->f_type != DTYPE_VNODE) {
+ error = EBADF;
+ goto out;
+ }
+ vp = (struct vnode *)fp->f_data;
+ proc_fdunlock(p);
+
+ pathlen = MAXPATHLEN;
+ MALLOC(pathbufp, char *, pathlen, M_TEMP, M_WAITOK);
+ if (pathbufp == NULL) {
+ error = ENOMEM;
+ goto outdrop;
+ }
+ 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 = hfs_is_backingstore (vp, &backingstore);
+ }
+ (void)vnode_put(vp);
+
+ if (error == 0) {
+ 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.
+ */
+ if (backingstore) {
+ error = EBUSY;
+ }
+ }
+ } else
+ (void)vnode_put(vp);
+ }
+ FREE(pathbufp, M_TEMP);
+ goto outdrop;