]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_subr.c
xnu-6153.61.1.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_subr.c
index e32310c8e302bae18a07955dcc43cef1b0da61bf..f8304f9ad51c5a0d22eca0ebda38f575e1426823 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2018 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
@@ -234,6 +234,8 @@ static void record_vp(vnode_t vp, int count);
 extern int bootarg_no_vnode_jetsam;    /* from bsd_init.c default value is 0 */
 #endif /* CONFIG_JETSAM && (DEVELOPMENT || DEBUG) */
 
+extern int bootarg_no_vnode_drain;    /* from bsd_init.c default value is 0 */
+
 boolean_t root_is_CF_drive = FALSE;
 
 #if CONFIG_TRIGGERS
@@ -250,13 +252,25 @@ TAILQ_HEAD(ragelst, vnode) vnode_rage_list;     /* vnode rapid age list */
 struct timeval rage_tv;
 int     rage_limit = 0;
 int     ragevnodes = 0;
+static  int vfs_unmountall_started = 0;
 
 #define RAGE_LIMIT_MIN  100
 #define RAGE_TIME_LIMIT 5
 
+/*
+ * ROSV definitions
+ * NOTE: These are shadowed from PlatformSupport definitions, but XNU
+ * builds standalone.
+ */
+#define PLATFORM_DATA_VOLUME_MOUNT_POINT "/System/Volumes/Data"
+#define PLATFORM_VM_VOLUME_MOUNT_POINT "/private/var/vm"
+
+
 struct mntlist mountlist;                       /* mounted filesystem list */
 static int nummounts = 0;
 
+static int print_busy_vnodes = 0;                               /* print out busy vnodes */
+
 #if DIAGNOSTIC
 #define VLISTCHECK(fun, vp, list)       \
        if ((vp)->v_freelist.tqe_prev == (struct vnode **)0xdeadb) \
@@ -477,6 +491,7 @@ int
 vnode_umount_preflight(mount_t mp, vnode_t skipvp, int flags)
 {
        vnode_t vp;
+       int ret = 0;
 
        TAILQ_FOREACH(vp, &mp->mnt_vnodelist, v_mntvnodes) {
                if (vp->v_type == VDIR) {
@@ -497,18 +512,28 @@ vnode_umount_preflight(mount_t mp, vnode_t skipvp, int flags)
 
                /* Look for busy vnode */
                if ((vp->v_usecount != 0) && ((vp->v_usecount - vp->v_kusecount) != 0)) {
-                       return 1;
+                       ret = 1;
+                       if (print_busy_vnodes && ((flags & FORCECLOSE) == 0)) {
+                               vprint("vnode_umount_preflight - busy vnode", vp);
+                       } else {
+                               return ret;
+                       }
                } else if (vp->v_iocount > 0) {
                        /* Busy if iocount is > 0 for more than 3 seconds */
                        tsleep(&vp->v_iocount, PVFS, "vnode_drain_network", 3 * hz);
                        if (vp->v_iocount > 0) {
-                               return 1;
+                               ret = 1;
+                               if (print_busy_vnodes && ((flags & FORCECLOSE) == 0)) {
+                                       vprint("vnode_umount_preflight - busy vnode", vp);
+                               } else {
+                                       return ret;
+                               }
                        }
                        continue;
                }
        }
 
-       return 0;
+       return ret;
 }
 
 /*
@@ -1259,6 +1284,98 @@ fail:
        return ENODEV;
 }
 
+/*
+ * Mount the data volume of an ROSV volume group
+ */
+int
+vfs_mount_rosv_data(void)
+{
+#if CONFIG_ROSV_STARTUP
+       int error = 0;
+       int do_rosv_mounts = 0;
+
+       error = vnode_get(rootvnode);
+       if (error) {
+               /* root must be mounted first */
+               printf("vnode_get(rootvnode) failed with error %d\n", error);
+               return error;
+       }
+
+       printf("NOTE: Attempting ROSV mount\n");
+       struct vfs_attr vfsattr;
+       VFSATTR_INIT(&vfsattr);
+       VFSATTR_WANTED(&vfsattr, f_capabilities);
+       if (vfs_getattr(rootvnode->v_mount, &vfsattr, vfs_context_kernel()) == 0 &&
+           VFSATTR_IS_SUPPORTED(&vfsattr, f_capabilities)) {
+               if ((vfsattr.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_VOL_GROUPS) &&
+                   (vfsattr.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_VOL_GROUPS)) {
+                       printf("NOTE: DETECTED ROSV CONFIG\n");
+                       do_rosv_mounts = 1;
+               }
+       }
+
+       if (!do_rosv_mounts) {
+               vnode_put(rootvnode);
+               //bail out if config not supported
+               return 0;
+       }
+
+       char datapath[] = PLATFORM_DATA_VOLUME_MOUNT_POINT; /* !const because of internal casting */
+
+       /* Mount the data volume */
+       printf("attempting kernel mount for data volume... \n");
+       error = kernel_mount(rootvnode->v_mount->mnt_vfsstat.f_fstypename, NULLVP, NULLVP,
+           datapath, (rootvnode->v_mount), 0, 0, (KERNEL_MOUNT_DATAVOL), vfs_context_kernel());
+
+       if (error) {
+               printf("Failed to mount data volume (%d)\n", error);
+       }
+
+       vnode_put(rootvnode);
+
+       return error;
+
+#else
+       return 0;
+#endif
+}
+
+/*
+ * Mount the VM volume of a container
+ */
+int
+vfs_mount_vm(void)
+{
+#if CONFIG_MOUNT_VM
+       int error = 0;
+
+       error = vnode_get(rootvnode);
+       if (error) {
+               /* root must be mounted first */
+               printf("vnode_get(rootvnode) failed with error %d\n", error);
+               return error;
+       }
+
+       char vmpath[] = PLATFORM_VM_VOLUME_MOUNT_POINT; /* !const because of internal casting */
+
+       /* Mount the VM volume */
+       printf("attempting kernel mount for vm volume... \n");
+       error = kernel_mount(rootvnode->v_mount->mnt_vfsstat.f_fstypename, NULLVP, NULLVP,
+           vmpath, (rootvnode->v_mount), 0, 0, (KERNEL_MOUNT_VMVOL), vfs_context_kernel());
+
+       if (error) {
+               printf("Failed to mount vm volume (%d)\n", error);
+       } else {
+               printf("mounted VM volume\n");
+       }
+
+       vnode_put(rootvnode);
+       return error;
+#else
+       return 0;
+#endif
+}
+
 /*
  * Lookup a mount point by filesystem identifier.
  */
@@ -2035,9 +2152,6 @@ done:
  * system error). If MNT_FORCE is specified, detach any active vnodes
  * that are found.
  */
-#if DIAGNOSTIC
-int busyprt = 0;        /* print out busy vnodes */
-#endif
 
 int
 vflush(struct mount *mp, struct vnode *skipvp, int flags)
@@ -2047,6 +2161,7 @@ vflush(struct mount *mp, struct vnode *skipvp, int flags)
        int reclaimed = 0;
        int retval;
        unsigned int vid;
+       bool first_try = true;
 
        /*
         * See comments in vnode_iterate() for the rationale for this lock
@@ -2191,11 +2306,12 @@ loop:
                        mount_lock(mp);
                        continue;
                }
-#if DIAGNOSTIC
-               if (busyprt) {
-                       vprint("vflush: busy vnode", vp);
+
+               /* log vnodes blocking unforced unmounts */
+               if (print_busy_vnodes && first_try && ((flags & FORCECLOSE) == 0)) {
+                       vprint("vflush - busy vnode", vp);
                }
-#endif
+
                vnode_unlock(vp);
                mount_lock(mp);
                busy++;
@@ -2206,6 +2322,7 @@ loop:
                busy = 0;
                reclaimed = 0;
                (void)vnode_iterate_reloadq(mp);
+               first_try = false;
                /* returned with mount lock held */
                goto loop;
        }
@@ -2213,6 +2330,7 @@ loop:
        /* if new vnodes were created in between retry the reclaim */
        if (vnode_iterate_reloadq(mp) != 0) {
                if (!(busy && ((flags & FORCECLOSE) == 0))) {
+                       first_try = false;
                        goto loop;
                }
        }
@@ -2367,7 +2485,7 @@ vclean(vnode_t vp, int flags)
        }
 
        // make sure the name & parent ptrs get cleaned out!
-       vnode_update_identity(vp, NULLVP, NULL, 0, 0, VNODE_UPDATE_PARENT | VNODE_UPDATE_NAME | VNODE_UPDATE_PURGE);
+       vnode_update_identity(vp, NULLVP, NULL, 0, 0, VNODE_UPDATE_PARENT | VNODE_UPDATE_NAME | VNODE_UPDATE_PURGE | VNODE_UPDATE_PURGEFIRMLINK);
 
        vnode_lock(vp);
 
@@ -2697,8 +2815,9 @@ vprint(const char *label, struct vnode *vp)
        if (label != NULL) {
                printf("%s: ", label);
        }
-       printf("type %s, usecount %d, writecount %d",
-           typename[vp->v_type], vp->v_usecount, vp->v_writecount);
+       printf("name %s type %s, usecount %d, writecount %d\n",
+           vp->v_name, typename[vp->v_type],
+           vp->v_usecount, vp->v_writecount);
        sbuf[0] = '\0';
        if (vp->v_flag & VROOT) {
                strlcat(sbuf, "|VROOT", sizeof(sbuf));
@@ -2719,7 +2838,7 @@ vprint(const char *label, struct vnode *vp)
                strlcat(sbuf, "|VALIASED", sizeof(sbuf));
        }
        if (sbuf[0] != '\0') {
-               printf(" flags (%s)", &sbuf[1]);
+               printf("vnode flags (%s\n", &sbuf[1]);
        }
 }
 
@@ -2772,6 +2891,29 @@ vn_getpath_fsenter_with_parent(struct vnode *dvp, struct vnode *vp, char *pathbu
        return build_path_with_parent(vp, dvp, pathbuf, *len, len, 0, vfs_context_current());
 }
 
+int
+vn_getpath_ext(struct vnode *vp, struct vnode *dvp, char *pathbuf, int *len, int flags)
+{
+       int bpflags = (flags & VN_GETPATH_FSENTER) ? 0 : BUILDPATH_NO_FS_ENTER;
+
+       if (flags && (flags != VN_GETPATH_FSENTER)) {
+               if (flags & VN_GETPATH_NO_FIRMLINK) {
+                       bpflags |= BUILDPATH_NO_FIRMLINK;;
+               }
+               if (flags & VN_GETPATH_VOLUME_RELATIVE) {
+                       bpflags |= (BUILDPATH_VOLUME_RELATIVE | BUILDPATH_NO_FIRMLINK);
+               }
+       }
+
+       return build_path_with_parent(vp, dvp, pathbuf, *len, len, bpflags, vfs_context_current());
+}
+
+int
+vn_getpath_no_firmlink(struct vnode *vp, char *pathbuf, int *len)
+{
+       return vn_getpath_ext(vp, NULLVP, pathbuf, len, VN_GETPATH_NO_FIRMLINK);
+}
+
 int
 vn_getcdhash(struct vnode *vp, off_t offset, unsigned char *cdhash)
 {
@@ -3172,6 +3314,8 @@ vfs_unmountall(void)
        int mounts, sec = 1;
        struct unmount_info ui;
 
+       vfs_unmountall_started = 1;
+
 retry:
        ui.u_errs = ui.u_busy = 0;
        vfs_iterate(VFS_ITERATE_CB_DROPREF | VFS_ITERATE_TAIL_FIRST, unmount_callback, &ui);
@@ -3260,6 +3404,7 @@ vfs_init_io_attributes(vnode_t devvp, mount_t mp)
        u_int32_t blksize;
        u_int64_t temp;
        u_int32_t features;
+       u_int64_t location = 0;
        vfs_context_t ctx = vfs_context_current();
        dk_corestorage_info_t cs_info;
        boolean_t cs_present = FALSE;;
@@ -3314,6 +3459,7 @@ vfs_init_io_attributes(vnode_t devvp, mount_t mp)
        if (VNOP_IOCTL(devvp, DKIOCISVIRTUAL, (caddr_t)&isvirtual, 0, ctx) == 0) {
                if (isvirtual) {
                        mp->mnt_kern_flag |= MNTK_VIRTUALDEV;
+                       mp->mnt_flag |= MNT_REMOVABLE;
                }
        }
        if (VNOP_IOCTL(devvp, DKIOCISSOLIDSTATE, (caddr_t)&isssd, 0, ctx) == 0) {
@@ -3497,6 +3643,13 @@ vfs_init_io_attributes(vnode_t devvp, mount_t mp)
                }
        }
 
+       if (VNOP_IOCTL(devvp, DKIOCGETLOCATION, (caddr_t)&location, 0, ctx) == 0) {
+               if (location & DK_LOCATION_EXTERNAL) {
+                       mp->mnt_ioflags |= MNT_IOFLAGS_PERIPHERAL_DRIVE;
+                       mp->mnt_flag |= MNT_REMOVABLE;
+               }
+       }
+
 #if CONFIG_IOSCHED
        if (iosched_enabled && (features & DK_FEATURE_PRIORITY)) {
                mp->mnt_ioflags |= MNT_IOFLAGS_IOSCHED_SUPPORTED;
@@ -3859,11 +4012,11 @@ out:
        return error;
 }
 
-static int      filt_fsattach(struct knote *kn, struct kevent_internal_s *kev);
+static int      filt_fsattach(struct knote *kn, struct kevent_qos_s *kev);
 static void     filt_fsdetach(struct knote *kn);
 static int      filt_fsevent(struct knote *kn, long hint);
-static int      filt_fstouch(struct knote *kn, struct kevent_internal_s *kev);
-static int      filt_fsprocess(struct knote *kn, struct filt_process_s *data, struct kevent_internal_s *kev);
+static int      filt_fstouch(struct knote *kn, struct kevent_qos_s *kev);
+static int      filt_fsprocess(struct knote *kn, struct kevent_qos_s *kev);
 SECURITY_READ_ONLY_EARLY(struct filterops) fs_filtops = {
        .f_attach = filt_fsattach,
        .f_detach = filt_fsdetach,
@@ -3873,8 +4026,11 @@ SECURITY_READ_ONLY_EARLY(struct filterops) fs_filtops = {
 };
 
 static int
-filt_fsattach(struct knote *kn, __unused struct kevent_internal_s *kev)
+filt_fsattach(struct knote *kn, __unused struct kevent_qos_s *kev)
 {
+       kn->kn_flags |= EV_CLEAR; /* automatic */
+       kn->kn_sdata = 0;         /* incoming data is ignored */
+
        lck_mtx_lock(fs_klist_lock);
        KNOTE_ATTACH(&fs_klist, kn);
        lck_mtx_unlock(fs_klist_lock);
@@ -3910,7 +4066,7 @@ filt_fsevent(struct knote *kn, long hint)
 }
 
 static int
-filt_fstouch(struct knote *kn, struct kevent_internal_s *kev)
+filt_fstouch(struct knote *kn, struct kevent_qos_s *kev)
 {
        int res;
 
@@ -3936,18 +4092,14 @@ filt_fstouch(struct knote *kn, struct kevent_internal_s *kev)
 }
 
 static int
-filt_fsprocess(struct knote *kn, struct filt_process_s *data, struct kevent_internal_s *kev)
+filt_fsprocess(struct knote *kn, struct kevent_qos_s *kev)
 {
-#pragma unused(data)
-       int res;
+       int res = 0;
 
        lck_mtx_lock(fs_klist_lock);
-       res = (kn->kn_fflags != 0);
-       if (res) {
-               *kev = kn->kn_kevent;
-               kn->kn_flags |= EV_CLEAR; /* automatic */
-               kn->kn_fflags = 0;
-               kn->kn_data = 0;
+       if (kn->kn_fflags) {
+               knote_fill_kevent(kn, kev, 0);
+               res = 1;
        }
        lck_mtx_unlock(fs_klist_lock);
        return res;
@@ -4062,6 +4214,12 @@ SYSCTL_INT(_vfs_generic, OID_AUTO, sync_timeout, CTLFLAG_RW | CTLFLAG_LOCKED, &s
 SYSCTL_NODE(_vfs_generic, VFS_CONF, conf,
     CTLFLAG_RD | CTLFLAG_LOCKED,
     sysctl_vfs_generic_conf, "");
+#if DEVELOPMENT || DEBUG
+SYSCTL_INT(_vfs_generic, OID_AUTO, print_busy_vnodes,
+    CTLTYPE_INT | CTLFLAG_RW,
+    &print_busy_vnodes, 0,
+    "VFS log busy vnodes blocking unmount");
+#endif
 
 /* Indicate that the root file system unmounted cleanly */
 static int vfs_root_unmounted_cleanly = 0;
@@ -4518,7 +4676,7 @@ steal_this_vp:
         */
        assert((vp->v_lflag & VL_LABELWAIT) != VL_LABELWAIT);
        assert((vp->v_lflag & VL_LABEL) != VL_LABEL);
-       if (vp->v_lflag & VL_LABELED) {
+       if (vp->v_lflag & VL_LABELED || vp->v_label != NULL) {
                vnode_lock_convert(vp);
                mac_vnode_label_recycle(vp);
        } else if (mac_vnode_label_init_needed(vp)) {
@@ -4817,7 +4975,25 @@ vnode_drain(vnode_t vp)
        vp->v_owner = current_thread();
 
        while (vp->v_iocount > 1) {
-               msleep(&vp->v_iocount, &vp->v_lock, PVFS, "vnode_drain", NULL);
+               if (bootarg_no_vnode_drain) {
+                       struct timespec ts = {.tv_sec = 10, .tv_nsec = 0};
+                       int error;
+
+                       if (vfs_unmountall_started) {
+                               ts.tv_sec = 1;
+                       }
+
+                       error = msleep(&vp->v_iocount, &vp->v_lock, PVFS, "vnode_drain_with_timeout", &ts);
+
+                       /* Try to deal with leaked iocounts under bootarg and shutting down */
+                       if (vp->v_iocount > 1 && error == EWOULDBLOCK &&
+                           ts.tv_sec == 1 && vp->v_numoutput == 0) {
+                               vp->v_iocount = 1;
+                               break;
+                       }
+               } else {
+                       msleep(&vp->v_iocount, &vp->v_lock, PVFS, "vnode_drain", NULL);
+               }
        }
 
        vp->v_lflag &= ~VL_DRAIN;
@@ -4987,6 +5163,13 @@ vnode_reclaim_internal(struct vnode * vp, int locked, int reuse, int flags)
 
        vn_clearunionwait(vp, 1);
 
+       if (vnode_istty(vp) && (flags & REVOKEALL) && vp->v_usecount &&
+           (vp->v_iocount > 1)) {
+               vnode_unlock(vp);
+               VNOP_IOCTL(vp, TIOCREVOKE, (caddr_t)NULL, 0, vfs_context_kernel());
+               vnode_lock(vp);
+       }
+
        vnode_drain(vp);
 
        isfifo = (vp->v_type == VFIFO);
@@ -5179,6 +5362,11 @@ vnode_create_internal(uint32_t flavor, uint32_t size, void *data, vnode_t *vpp,
        record_vp(vp, 1);
 #endif
 
+#if CONFIG_FIRMLINKS
+       vp->v_fmlink = NULLVP;
+#endif
+       vp->v_flag &= ~VFMLINKTARGET;
+
 #if CONFIG_TRIGGERS
        /*
         * For trigger vnodes, attach trigger info to vnode
@@ -5462,6 +5650,7 @@ vfs_iterate(int flags, int (*callout)(mount_t, void *), void *arg)
        void * allocmem;
        int indx_start, indx_stop, indx_incr;
        int cb_dropref = (flags & VFS_ITERATE_CB_DROPREF);
+       int noskip_unmount = (flags & VFS_ITERATE_NOSKIP_UNMOUNT);
 
        count = mount_getvfscnt();
        count += 10;
@@ -5493,7 +5682,8 @@ vfs_iterate(int flags, int (*callout)(mount_t, void *), void *arg)
                        continue;
                }
                mount_lock(mp);
-               if (mp->mnt_lflag & (MNT_LDEAD | MNT_LUNMOUNT)) {
+               if ((mp->mnt_lflag & MNT_LDEAD) ||
+                   (!noskip_unmount && (mp->mnt_lflag & MNT_LUNMOUNT))) {
                        mount_unlock(mp);
                        mount_iterdrop(mp);
                        continue;
@@ -5721,7 +5911,8 @@ out:
 }
 
 errno_t
-vnode_lookup(const char *path, int flags, vnode_t *vpp, vfs_context_t ctx)
+vnode_lookupat(const char *path, int flags, vnode_t *vpp, vfs_context_t ctx,
+    vnode_t start_dvp)
 {
        struct nameidata nd;
        int error;
@@ -5749,15 +5940,29 @@ vnode_lookup(const char *path, int flags, vnode_t *vpp, vfs_context_t ctx)
        NDINIT(&nd, LOOKUP, OP_LOOKUP, ndflags, UIO_SYSSPACE,
            CAST_USER_ADDR_T(path), ctx);
 
+       if (start_dvp && (path[0] != '/')) {
+               nd.ni_dvp = start_dvp;
+               nd.ni_cnd.cn_flags |= USEDVP;
+       }
+
        if ((error = namei(&nd))) {
                return error;
        }
+
+       nd.ni_cnd.cn_flags &= ~USEDVP;
+
        *vpp = nd.ni_vp;
        nameidone(&nd);
 
        return 0;
 }
 
+errno_t
+vnode_lookup(const char *path, int flags, vnode_t *vpp, vfs_context_t ctx)
+{
+       return vnode_lookupat(path, flags, vpp, ctx, NULLVP);
+}
+
 errno_t
 vnode_open(const char *path, int fmode, int cmode, int flags, vnode_t *vpp, vfs_context_t ctx)
 {
@@ -7673,7 +7878,7 @@ vnode_authorize_checkimmutable(mount_t mp, struct vnode_attr *vap, int rights, i
                        /* check for no-EA filesystems */
                        if ((rights & KAUTH_VNODE_WRITE_EXTATTRIBUTES) &&
                            (vfs_flags(mp) & MNT_NOUSERXATTR)) {
-                               KAUTH_DEBUG("%p    DENIED - filesystem disallowed extended attributes", vp);
+                               KAUTH_DEBUG("%p    DENIED - filesystem disallowed extended attributes", vap);
                                error = EACCES;  /* User attributes disabled */
                                goto out;
                        }
@@ -7694,7 +7899,7 @@ vnode_authorize_checkimmutable(mount_t mp, struct vnode_attr *vap, int rights, i
                        }
                }
                if ((error = vnode_immutable(vap, append, ignore)) != 0) {
-                       KAUTH_DEBUG("%p    DENIED - file is immutable", vp);
+                       KAUTH_DEBUG("%p    DENIED - file is immutable", vap);
                        goto out;
                }
        }
@@ -7954,14 +8159,14 @@ vnode_attr_authorize_internal(vauth_ctx vcp, mount_t mp,
                    VATTR_IS_SUPPORTED(vcp->vap, va_mode) &&
                    !(vcp->vap->va_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
                        result = EPERM;
-                       KAUTH_DEBUG("%p    DENIED - root execute requires at least one x bit in 0x%x", vp, va.va_mode);
+                       KAUTH_DEBUG("%p    DENIED - root execute requires at least one x bit in 0x%x", vcp, vcp->vap->va_mode);
                        goto out;
                }
 
                /* Assume that there were DENYs so we don't wrongly cache KAUTH_VNODE_SEARCHBYANYONE */
                *found_deny = TRUE;
 
-               KAUTH_DEBUG("%p    ALLOWED - caller is superuser", vp);
+               KAUTH_DEBUG("%p    ALLOWED - caller is superuser", vcp);
        }
 out:
        return result;
@@ -8454,6 +8659,7 @@ vnode_authattr_new_internal(vnode_t dvp, struct vnode_attr *vap, int noauth, uin
 
 
        if (VATTR_IS_ACTIVE(vap, va_flags)) {
+               vap->va_flags &= ~SF_SYNTHETIC;
                if (has_priv_suser) {
                        if ((vap->va_flags & (UF_SETTABLE | SF_SETTABLE)) != vap->va_flags) {
                                error = EPERM;
@@ -8814,6 +9020,8 @@ vnode_authattr(vnode_t vp, struct vnode_attr *vap, kauth_action_t *actionp, vfs_
         */
        if (VATTR_IS_ACTIVE(vap, va_flags)) {
                /* compute changing flags bits */
+               vap->va_flags &= ~SF_SYNTHETIC;
+               ova.va_flags &= ~SF_SYNTHETIC;
                if (VATTR_IS_SUPPORTED(&ova, va_flags)) {
                        fdelta = vap->va_flags ^ ova.va_flags;
                } else {
@@ -9040,12 +9248,12 @@ no_guuid_change:
                }
 
                /* chown always clears setuid/gid bits. An exception is made for
-                * setattrlist executed by a root process to set <uid, gid, mode> on a file:
+                * setattrlist which can set both at the same time: <uid, gid, mode> on a file:
                 * setattrlist is allowed to set the new mode on the file and change (chown)
                 * uid/gid.
                 */
                if (newmode & (S_ISUID | S_ISGID)) {
-                       if (!VATTR_IS_ACTIVE(vap, va_mode) || !has_priv_suser) {
+                       if (!VATTR_IS_ACTIVE(vap, va_mode)) {
                                KAUTH_DEBUG("CHOWN - masking setugid bits from mode %o to %o",
                                    newmode, newmode & ~(S_ISUID | S_ISGID));
                                newmode &= ~(S_ISUID | S_ISGID);
@@ -9195,6 +9403,59 @@ vn_clearunionwait(vnode_t vp, int locked)
        }
 }
 
+int
+vnode_materialize_dataless_file(vnode_t vp, uint64_t op_type)
+{
+       int error;
+
+       /* Swap files are special; ignore them */
+       if (vnode_isswap(vp)) {
+               return 0;
+       }
+
+       error = resolve_nspace_item(vp,
+           op_type | NAMESPACE_HANDLER_NSPACE_EVENT);
+
+       /*
+        * The file resolver owns the logic about what error to return
+        * to the caller.  We only need to handle a couple of special
+        * cases here:
+        */
+       if (error == EJUSTRETURN) {
+               /*
+                * The requesting process is allowed to interact with
+                * dataless objects.  Make a couple of sanity-checks
+                * here to ensure the action makes sense.
+                */
+               switch (op_type) {
+               case NAMESPACE_HANDLER_WRITE_OP:
+               case NAMESPACE_HANDLER_TRUNCATE_OP:
+               case NAMESPACE_HANDLER_RENAME_OP:
+                       /*
+                        * This handles the case of the resolver itself
+                        * writing data to the file (or throwing it
+                        * away).
+                        */
+                       error = 0;
+                       break;
+               case NAMESPACE_HANDLER_READ_OP:
+                       /*
+                        * This handles the case of the resolver needing
+                        * to look up inside of a dataless directory while
+                        * it's in the process of materializing it (for
+                        * example, creating files or directories).
+                        */
+                       error = (vnode_vtype(vp) == VDIR) ? 0 : EBADF;
+                       break;
+               default:
+                       error = EBADF;
+                       break;
+               }
+       }
+
+       return error;
+}
+
 /*
  * Removes orphaned apple double files during a rmdir
  * Works by:
@@ -9233,6 +9494,15 @@ rmdir_remove_orphaned_appleDouble(vnode_t vp, vfs_context_t ctx, int * restart_f
                return error;
        }
 
+       /*
+        * Prevent dataless fault materialization while we have
+        * a suspended vnode.
+        */
+       uthread_t ut = get_bsdthread_info(current_thread());
+       bool saved_nodatalessfaults =
+           (ut->uu_flag & UT_NSPACE_NODATALESSFAULTS) ? true : false;
+       ut->uu_flag |= UT_NSPACE_NODATALESSFAULTS;
+
        /*
         * set up UIO
         */
@@ -9411,8 +9681,11 @@ outsc:
        }
        FREE(rbuf, M_TEMP);
 
-       vnode_resume(vp);
+       if (saved_nodatalessfaults == false) {
+               ut->uu_flag &= ~UT_NSPACE_NODATALESSFAULTS;
+       }
 
+       vnode_resume(vp);
 
        return error;
 }
@@ -9883,9 +10156,16 @@ vnode_trigger_resolve(vnode_t vp, struct nameidata *ndp, vfs_context_t ctx)
        lck_mtx_unlock(&rp->vr_lock);
 
 #if CONFIG_MACF
-       int rv = mac_vnode_check_trigger_resolve(ctx, vp, &ndp->ni_cnd);
-       if (rv != 0) {
-               return rv;
+       if ((rp->vr_flags & VNT_KERN_RESOLVE) == 0) {
+               /*
+                * VNT_KERN_RESOLVE indicates this trigger has no parameters
+                * at the discression of the accessing process other than
+                * the act of access. All other triggers must be checked
+                */
+               int rv = mac_vnode_check_trigger_resolve(ctx, vp, &ndp->ni_cnd);
+               if (rv != 0) {
+                       return rv;
+               }
        }
 #endif