+ /*
+ * 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, USEDVP | AUDITVNPATH1, UIO_USERSPACE, pathname, &context);
+ nd.ni_dvp = vp;
+
+ error = unlink1(&context, &nd, 0);
+
+ vnode_put(vp);
+ break;
+
+ }
+
+ case F_ADDSIGS: {
+ struct user_fsignatures fs;
+ kern_return_t kr;
+ vm_address_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 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;
+ }
+
+#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 = kmem_alloc(kernel_map,
+ &kernel_blob_addr,
+ kernel_blob_size);
+ if (kr != KERN_SUCCESS) {
+ error = ENOMEM;
+ vnode_put(vp);
+ goto outdrop;
+ }
+
+ error = copyin(fs.fs_blob_start,
+ (void *) kernel_blob_addr,
+ kernel_blob_size);
+ if (error) {
+ kmem_free(kernel_map,
+ 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) {
+ kmem_free(kernel_map,
+ kernel_blob_addr,
+ kernel_blob_size);
+ } else {
+ /* ubc_blob_add() was consumed "kernel_blob_addr" */
+ }
+
+ (void) vnode_put(vp);
+ break;
+ }
+
+ case F_MARKDEPENDENCY: {
+ struct vnode *root_vp;
+ 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;
+ }
+
+ // the passed in vnode must be the root dir of the file system
+ if (VFS_ROOT(vp->v_mount, &root_vp, ctx) != 0 || vp != root_vp) {
+ error = EINVAL;
+ vnode_put(vp);
+ goto outdrop;
+ }
+ vnode_put(root_vp);
+
+ // 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;
+ }
+
+ default:
+ if (uap->cmd < FCNTL_FS_SPECIFIC_BASE) {
+ error = EINVAL;
+ goto out;
+ }
+
+ // if it's a fs-specific fcntl() then just pass it through
+
+ if (fp->f_type != DTYPE_VNODE) {
+ error = EBADF;
+ goto out;
+ }
+ vp = (struct vnode *)fp->f_data;
+ proc_fdunlock(p);
+
+ if ( (error = vnode_getwithref(vp)) == 0 ) {
+ error = VNOP_IOCTL(vp, uap->cmd, CAST_DOWN(caddr_t, argp), 0, &context);
+
+ (void)vnode_put(vp);
+ }
+ break;
+
+ }
+
+outdrop:
+ AUDIT_ARG(vnpath_withref, vp, ARG_VNODE1);
+ fp_drop(p, fd, fp, 0);
+ return(error);
+out:
+ fp_drop(p, fd, fp, 1);
+ proc_fdunlock(p);
+ return(error);
+}