]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/kpi_vfs.c
xnu-4903.231.4.tar.gz
[apple/xnu.git] / bsd / vfs / kpi_vfs.c
index 8e9e760149fbffc382fa3ea07f3edbc2505aa7a8..f09e98f741522138fe9e5ba0448a9bfa7a1a0895 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
 #include <sys/user.h>
 #include <sys/lockf.h>
 #include <sys/xattr.h>
+#include <sys/kdebug.h>
 
 #include <kern/assert.h>
 #include <kern/kalloc.h>
 #include <kern/task.h>
+#include <kern/policy_internal.h>
 
 #include <libkern/OSByteOrder.h>
 
 #include <security/mac_framework.h>
 #endif
 
+#if NULLFS
+#include <miscfs/nullfs/nullfs.h>
+#endif
+
 #include <sys/sdt.h>
 
 #define ESUCCESS 0
@@ -1595,12 +1601,16 @@ vfs_ctx_skipatime (vfs_context_t ctx) {
                if (proc->p_lflag & P_LRAGE_VNODES) {
                        return 1;
                }
-               
+
                if (ut) {
-                       if  (ut->uu_flag & UT_RAGE_VNODES) {
+                       if  (ut->uu_flag & (UT_RAGE_VNODES | UT_ATIME_UPDATE)) {
                                return 1;
                        }
                }
+
+               if (proc->p_vfs_iopolicy & P_VFS_IOPOLICY_ATIME_UPDATES) {
+                       return 1;
+               }
        }
        return 0;
 }
@@ -2168,6 +2178,11 @@ vnode_get_filesec(vnode_t vp, kauth_filesec_t *fsecp, vfs_context_t ctx)
                                
        /* how many entries would fit? */
        fsec_size = KAUTH_FILESEC_COUNT(xsize);
+       if (fsec_size > KAUTH_ACL_MAX_ENTRIES) {
+               KAUTH_DEBUG("    ERROR - Bogus (too large) kauth_fiilesec_t: %ld bytes", xsize);
+               error = 0;
+               goto out;
+       }
 
        /* get buffer and uio */
        if (((fsec = kauth_filesec_alloc(fsec_size)) == NULL) ||
@@ -2314,6 +2329,7 @@ out:
 /*
  * Returns:    0                       Success
  *             ENOMEM                  Not enough space [only if has filesec]
+ *             EINVAL                  Requested unknown attributes
  *             VNOP_GETATTR:           ???
  *             vnode_get_filesec:      ???
  *             kauth_cred_guid2uid:    ???
@@ -2329,6 +2345,12 @@ vnode_getattr(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx)
        uid_t   nuid;
        gid_t   ngid;
 
+       /*
+        * Reject attempts to fetch unknown attributes.
+        */
+       if (vap->va_active & ~VNODE_ATTR_ALL)
+               return (EINVAL);
+
        /* don't ask for extended security data if the filesystem doesn't support it */
        if (!vfs_extendedsecurity(vnode_mount(vp))) {
                VATTR_CLEAR_ACTIVE(vap, va_acl);
@@ -2555,7 +2577,18 @@ out:
 int
 vnode_setattr(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx)
 {
-       int     error, is_perm_change=0;
+       int     error;
+#if CONFIG_FSE
+       uint64_t active;
+       int     is_perm_change = 0;
+       int     is_stat_change = 0;
+#endif
+
+       /*
+        * Reject attempts to set unknown attributes.
+        */
+       if (vap->va_active & ~VNODE_ATTR_ALL)
+               return (EINVAL);
 
        /*
         * Make sure the filesystem is mounted R/W.
@@ -2566,6 +2599,19 @@ vnode_setattr(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx)
                goto out;
        }
 
+#if DEVELOPMENT || DEBUG
+       /*
+        * XXX VSWAP: Check for entitlements or special flag here
+        * so we can restrict access appropriately.
+        */
+#else /* DEVELOPMENT || DEBUG */
+
+       if (vnode_isswap(vp) && (ctx != vfs_context_kernel())) {
+               error = EPERM;
+               goto out;
+       }
+#endif /* DEVELOPMENT || DEBUG */
+
 #if NAMEDSTREAMS
        /* For streams, va_data_size is the only setable attribute. */
        if ((vp->v_flag & VISNAMEDSTREAM) && (vap->va_active != VNODE_ATTR_va_data_size)) {
@@ -2602,11 +2648,6 @@ vnode_setattr(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx)
                VATTR_CLEAR_ACTIVE(vap, va_gid);
        }
 
-       if (   VATTR_IS_ACTIVE(vap, va_uid)  || VATTR_IS_ACTIVE(vap, va_gid)
-           || VATTR_IS_ACTIVE(vap, va_mode) || VATTR_IS_ACTIVE(vap, va_acl)) {
-           is_perm_change = 1;
-       }
-       
        /*
         * Make sure that extended security is enabled if we're going to try
         * to set any.
@@ -2623,23 +2664,55 @@ vnode_setattr(vnode_t vp, struct vnode_attr *vap, vfs_context_t ctx)
            vap->va_flags &= (SF_SUPPORTED | UF_SETTABLE);
        }
 
+#if CONFIG_FSE
+       /*
+        * Remember all of the active attributes that we're
+        * attempting to modify.
+        */
+       active = vap->va_active & ~VNODE_ATTR_RDONLY;
+#endif
+
        error = VNOP_SETATTR(vp, vap, ctx);
 
        if ((error == 0) && !VATTR_ALL_SUPPORTED(vap))
                error = vnode_setattr_fallback(vp, vap, ctx);
 
 #if CONFIG_FSE
-       // only send a stat_changed event if this is more than
-       // just an access or backup time update
-       if (error == 0 && (vap->va_active != VNODE_ATTR_BIT(va_access_time)) && (vap->va_active != VNODE_ATTR_BIT(va_backup_time))) {
+#define        PERMISSION_BITS (VNODE_ATTR_BIT(va_uid) | VNODE_ATTR_BIT(va_uuuid) | \
+                        VNODE_ATTR_BIT(va_gid) | VNODE_ATTR_BIT(va_guuid) | \
+                        VNODE_ATTR_BIT(va_mode) | VNODE_ATTR_BIT(va_acl))
+
+       /*
+        * Now that we've changed them, decide whether to send an
+        * FSevent.
+        */
+       if ((active & PERMISSION_BITS) & vap->va_supported) {
+               is_perm_change = 1;
+       } else {
+               /*
+                * We've already checked the permission bits, and we
+                * also want to filter out access time / backup time
+                * changes.
+                */
+               active &= ~(PERMISSION_BITS |
+                           VNODE_ATTR_BIT(va_access_time) |
+                           VNODE_ATTR_BIT(va_backup_time));
+
+               /* Anything left to notify about? */
+               if (active & vap->va_supported)
+                       is_stat_change = 1;
+       }
+
+       if (error == 0) {
            if (is_perm_change) {
                if (need_fsevent(FSE_CHOWN, vp)) {
                    add_fsevent(FSE_CHOWN, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
                }
-           } else if(need_fsevent(FSE_STAT_CHANGED, vp)) {
+           } else if (is_stat_change && need_fsevent(FSE_STAT_CHANGED, vp)) {
                add_fsevent(FSE_STAT_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
            }
        }
+#undef PERMISSION_BITS
 #endif
 
 out:
@@ -2841,6 +2914,20 @@ vnode_ismonitored(vnode_t vp) {
        return (vp->v_knotes.slh_first != NULL);
 }
 
+int
+vnode_getbackingvnode(vnode_t in_vp, vnode_t* out_vpp)
+{
+       if (out_vpp) {
+               *out_vpp = NULLVP;
+       }
+#if NULLFS
+       return nullfs_getbackingvnode(in_vp, out_vpp);
+#else
+#pragma unused(in_vp)
+       return ENOENT;
+#endif
+}
+
 /*
  * Initialize a struct vnode_attr and activate the attributes required
  * by the vnode_notify() call.
@@ -3940,37 +4027,35 @@ vn_rename(struct vnode *fdvp, struct vnode **fvpp, struct componentname *fcnp, s
                 * in the rename syscall. It's OK if the source file does not exist, since this
                 * is only for AppleDouble files.
                 */
-               if (xfromname != NULL) {
-                       MALLOC(fromnd, struct nameidata *, sizeof (struct nameidata), M_TEMP, M_WAITOK);
-                       NDINIT(fromnd, RENAME, OP_RENAME, NOFOLLOW | USEDVP | CN_NBMOUNTLOOK,
-                              UIO_SYSSPACE, CAST_USER_ADDR_T(xfromname), ctx);
-                       fromnd->ni_dvp = fdvp;
-                       error = namei(fromnd);
-               
-                       /* 
-                        * If there was an error looking up source attribute file, 
-                        * we'll behave as if it didn't exist. 
-                        */
+               MALLOC(fromnd, struct nameidata *, sizeof (struct nameidata), M_TEMP, M_WAITOK);
+               NDINIT(fromnd, RENAME, OP_RENAME, NOFOLLOW | USEDVP | CN_NBMOUNTLOOK,
+                               UIO_SYSSPACE, CAST_USER_ADDR_T(xfromname), ctx);
+               fromnd->ni_dvp = fdvp;
+               error = namei(fromnd);
 
-                       if (error == 0) {
-                               if (fromnd->ni_vp) {
-                                       /* src_attr_vp indicates need to call vnode_put / nameidone later */
-                                       src_attr_vp = fromnd->ni_vp;
-                                                                               
-                                       if (fromnd->ni_vp->v_type != VREG) {
-                                               src_attr_vp = NULLVP;
-                                               vnode_put(fromnd->ni_vp);
-                                       }
-                               } 
-                               /*
-                                * Either we got an invalid vnode type (not a regular file) or the namei lookup 
-                                * suppressed ENOENT as a valid error since we're renaming. Either way, we don't 
-                                * have a vnode here, so we drop our namei buffer for the source attribute file
-                                */
-                               if (src_attr_vp == NULLVP) {
-                                       nameidone(fromnd);
+               /*
+                * If there was an error looking up source attribute file,
+                * we'll behave as if it didn't exist.
+                */
+
+               if (error == 0) {
+                       if (fromnd->ni_vp) {
+                               /* src_attr_vp indicates need to call vnode_put / nameidone later */
+                               src_attr_vp = fromnd->ni_vp;
+
+                               if (fromnd->ni_vp->v_type != VREG) {
+                                       src_attr_vp = NULLVP;
+                                       vnode_put(fromnd->ni_vp);
                                }
                        }
+                       /*
+                        * Either we got an invalid vnode type (not a regular file) or the namei lookup
+                        * suppressed ENOENT as a valid error since we're renaming. Either way, we don't
+                        * have a vnode here, so we drop our namei buffer for the source attribute file
+                        */
+                       if (src_attr_vp == NULLVP) {
+                               nameidone(fromnd);
+                       }
                }
        }
 #endif /* CONFIG_APPLEDOUBLE */
@@ -4000,14 +4085,17 @@ vn_rename(struct vnode *fdvp, struct vnode **fvpp, struct componentname *fcnp, s
         */
        if (_err == 0) {
                _err = vnode_flags(tdvp, &tdfflags, ctx);
-               if (_err == 0 && (tdfflags & SF_RESTRICTED)) {
-                       uint32_t fflags;
-                       _err = vnode_flags(*fvpp, &fflags, ctx);
-                       if (_err == 0 && !(fflags & SF_RESTRICTED)) {
-                               struct vnode_attr va;
-                               VATTR_INIT(&va);
-                               VATTR_SET(&va, va_flags, fflags | SF_RESTRICTED);
-                               _err = vnode_setattr(*fvpp, &va, ctx);
+               if (_err == 0) {
+                       uint32_t inherit_flags = tdfflags & (UF_DATAVAULT | SF_RESTRICTED);
+                       if (inherit_flags) {
+                               uint32_t fflags;
+                               _err = vnode_flags(*fvpp, &fflags, ctx);
+                               if (_err == 0 && fflags != (fflags | inherit_flags)) {
+                                       struct vnode_attr va;
+                                       VATTR_INIT(&va);
+                                       VATTR_SET(&va, va_flags, fflags | inherit_flags);
+                                       _err = vnode_setattr(*fvpp, &va, ctx);
+                               }
                        }
                }
        }
@@ -5370,6 +5458,7 @@ struct vnop_clonefile_args {
                        vnode_t sdvp, /* source directory vnode pointer (optional) */
                        mount_t mp, /* mount point of filesystem */
                        dir_clone_authorizer_op_t vattr_op, /* specific operation requested : setup, authorization or cleanup  */
+                       uint32_t flags; /* value passed in a_flags to the VNOP */
                        vfs_context_t ctx,              /* As passed to VNOP */
                        void *reserved);                /* Always NULL */
        void *a_reserved;               /* Currently unused */
@@ -5399,8 +5488,11 @@ VNOP_CLONEFILE(vnode_t fvp, vnode_t dvp, vnode_t *vpp,
 
        _err = (*dvp->v_op[vnop_clonefile_desc.vdesc_offset])(&a);
 
-       if (_err == 0 && *vpp)
+       if (_err == 0 && *vpp) {
                DTRACE_FSINFO(clonefile, vnode_t, *vpp);
+               if (kdebug_enable)
+                       kdebug_lookup(*vpp, cnp);
+       }
 
        post_event_if_success(dvp, _err, NOTE_WRITE);