]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_attrlist.c
xnu-4903.221.2.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_attrlist.c
index 38df5a58daa33e69a9b51adddc7b4671a605c6e4..cd8cbacad02a77712121ceb8ffc4688285fa2497 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 1995-2018 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -51,6 +51,7 @@
 #include <sys/fsevents.h>
 #include <kern/kalloc.h>
 #include <miscfs/specfs/specdev.h>
+#include <security/audit/audit.h>
 
 #if CONFIG_MACF
 #include <security/mac_framework.h>
@@ -269,7 +270,7 @@ attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count
 #define ATTR_PACK8(AB, V)                                                 \
        do {                                                              \
                if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 8) {   \
-                       *(uint64_t *)AB.fixedcursor = *(uint64_t *)&V;    \
+                       memcpy(AB.fixedcursor, &V, 8);                    \
                        AB.fixedcursor += 8;                              \
                }                                                         \
        } while (0)
@@ -339,7 +340,7 @@ static struct getvolattrlist_attrtab getvolattrlist_common_tab[] = {
 static struct getvolattrlist_attrtab getvolattrlist_vol_tab[] = {
        {ATTR_VOL_FSTYPE,               0,                                              sizeof(uint32_t)},
        {ATTR_VOL_SIGNATURE,            VFSATTR_BIT(f_signature),                       sizeof(uint32_t)},
-       {ATTR_VOL_SIZE,                 VFSATTR_BIT(f_blocks),                          sizeof(off_t)},
+       {ATTR_VOL_SIZE,                         VFSATTR_BIT(f_blocks)  |  VFSATTR_BIT(f_bsize),                         sizeof(off_t)},
        {ATTR_VOL_SPACEFREE,            VFSATTR_BIT(f_bfree) | VFSATTR_BIT(f_bsize),    sizeof(off_t)},
        {ATTR_VOL_SPACEAVAIL,           VFSATTR_BIT(f_bavail) | VFSATTR_BIT(f_bsize),   sizeof(off_t)},
        {ATTR_VOL_MINALLOCATION,        VFSATTR_BIT(f_bsize),                           sizeof(off_t)},
@@ -356,8 +357,8 @@ static struct getvolattrlist_attrtab getvolattrlist_vol_tab[] = {
        {ATTR_VOL_ENCODINGSUSED,        0,                                              sizeof(uint64_t)},
        {ATTR_VOL_CAPABILITIES,         VFSATTR_BIT(f_capabilities),                    sizeof(vol_capabilities_attr_t)},
        {ATTR_VOL_UUID,                 VFSATTR_BIT(f_uuid),                            sizeof(uuid_t)},
-       {ATTR_VOL_QUOTA_SIZE,           VFSATTR_BIT(f_quota),                           sizeof(off_t)},
-       {ATTR_VOL_RESERVED_SIZE,        VFSATTR_BIT(f_reserved),                        sizeof(off_t)},
+       {ATTR_VOL_QUOTA_SIZE,           VFSATTR_BIT(f_quota) | VFSATTR_BIT(f_bsize),    sizeof(off_t)},
+       {ATTR_VOL_RESERVED_SIZE,        VFSATTR_BIT(f_reserved) | VFSATTR_BIT(f_bsize), sizeof(off_t)},
        {ATTR_VOL_ATTRIBUTES,           VFSATTR_BIT(f_attributes),                      sizeof(vol_attributes_attr_t)},
        {ATTR_VOL_INFO, 0, 0},
        {0, 0, 0}
@@ -536,6 +537,7 @@ static struct getattrlist_attrtab getattrlist_file_tab[] = {
 static struct getattrlist_attrtab getattrlist_common_extended_tab[] = {
        {ATTR_CMNEXT_RELPATH,           0,                                                      sizeof(struct attrreference),   KAUTH_VNODE_READ_ATTRIBUTES},
        {ATTR_CMNEXT_PRIVATESIZE,       VATTR_BIT(va_private_size),     sizeof(off_t),                                  KAUTH_VNODE_READ_ATTRIBUTES},
+       {ATTR_CMNEXT_LINKID,            VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid),            sizeof(uint64_t),       KAUTH_VNODE_READ_ATTRIBUTES},
        {0, 0, 0, 0}
 };
 
@@ -596,7 +598,7 @@ static struct getattrlist_attrtab getattrlistbulk_common_extended_tab[] = {
                                 ATTR_CMN_DOCUMENT_ID | ATTR_CMN_GEN_COUNT | \
                                 ATTR_CMN_DATA_PROTECT_FLAGS)
 
-#define VFS_DFLT_ATT_CMN_EXT   (ATTR_CMNEXT_PRIVATESIZE)
+#define VFS_DFLT_ATTR_CMN_EXT  (ATTR_CMNEXT_PRIVATESIZE | ATTR_CMNEXT_LINKID)
 
 #define VFS_DFLT_ATTR_DIR      (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
 
@@ -1006,7 +1008,7 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                                attrp->validattr.volattr = VFS_DFLT_ATTR_VOL;
                                attrp->validattr.dirattr = VFS_DFLT_ATTR_DIR;
                                attrp->validattr.fileattr = VFS_DFLT_ATTR_FILE;
-                               attrp->validattr.forkattr = 0;
+                               attrp->validattr.forkattr = VFS_DFLT_ATTR_CMN_EXT;
                
                                attrp->nativeattr.commonattr =  0;
                                attrp->nativeattr.volattr = 0;
@@ -1134,12 +1136,33 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
         * Note that since we won't ever copy out more than the caller requested,
         * we never need to allocate more than they offer.
         */
-       ab.allocated = ulmin(bufferSize, fixedsize + varsize);
-       if (ab.allocated > ATTR_MAX_BUFFER) {
+       ab.allocated = fixedsize + varsize;
+       if (((size_t)ab.allocated) > ATTR_MAX_BUFFER) {
                error = ENOMEM;
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
                goto out;
        }
+
+       if (return_valid &&
+           (ab.allocated < (ssize_t)(sizeof(uint32_t) + sizeof(attribute_set_t))) &&
+           !(options & FSOPT_REPORT_FULLSIZE)) {
+               uint32_t num_bytes_valid = sizeof(uint32_t);
+               /*
+                * Not enough to return anything and we don't have to report
+                * how much space is needed. Get out now.
+                * N.B. - We have only been called after having verified that
+                * attributeBuffer is at least sizeof(uint32_t);
+                */
+               if (UIO_SEG_IS_USER_SPACE(segflg)) {
+                       error = copyout(&num_bytes_valid,
+                           CAST_USER_ADDR_T(attributeBuffer), num_bytes_valid);
+               } else {
+                       bcopy(&num_bytes_valid, (void *)attributeBuffer,
+                           (size_t)num_bytes_valid);
+               }
+               goto out;
+       }
+
        MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_ZERO | M_WAITOK);
        if (ab.base == NULL) {
                error = ENOMEM;
@@ -1159,6 +1182,10 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
        ab.needed = fixedsize + varsize;
 
        /* common attributes **************************************************/
+       if (alp->commonattr & ATTR_CMN_ERROR) {
+               ATTR_PACK4(ab, 0);
+               ab.actual.commonattr |= ATTR_CMN_ERROR;
+       }
        if (alp->commonattr & ATTR_CMN_NAME) {
                attrlist_pack_string(&ab, cnp, cnl);
                ab.actual.commonattr |= ATTR_CMN_NAME;
@@ -1393,6 +1420,21 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                        vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] &= ~VOL_CAP_INT_EXTENDED_SECURITY;
                }
                vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
+
+               /*
+                * if the filesystem doesn't mark either VOL_CAP_FMT_NO_IMMUTABLE_FILES
+                * or VOL_CAP_FMT_NO_PERMISSIONS as valid, assume they're not supported
+                */
+               if (!(vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_NO_IMMUTABLE_FILES)) {
+                       vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~VOL_CAP_FMT_NO_IMMUTABLE_FILES;
+                       vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_IMMUTABLE_FILES;
+               }
+
+               if (!(vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_NO_PERMISSIONS)) {
+                       vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~VOL_CAP_FMT_NO_PERMISSIONS;
+                       vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_PERMISSIONS;
+               }
+
                ATTR_PACK(&ab, vs.f_capabilities);
                ab.actual.volattr |= ATTR_VOL_CAPABILITIES;
        }
@@ -1439,10 +1481,11 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
         * of the result buffer, even if we copied less out.  The caller knows how big a buffer
         * they gave us, so they can always check for truncation themselves.
         */
-       *(uint32_t *)ab.base = (options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
-       
+       *(uint32_t *)ab.base = (options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(bufferSize, ab.needed);
+
        /* Return attribute set output if requested. */
-       if (return_valid) {
+       if (return_valid &&
+           (ab.allocated >= (ssize_t)(sizeof(uint32_t) + sizeof(ab.actual)))) {
                ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS;
                if (pack_invalid) {
                        /* Only report the attributes that are valid */
@@ -1454,9 +1497,9 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
 
        if (UIO_SEG_IS_USER_SPACE(segflg))
                error = copyout(ab.base, CAST_USER_ADDR_T(attributeBuffer),
-                               ab.allocated);
+                               ulmin(bufferSize, ab.needed));
        else
-               bcopy(ab.base, (void *)attributeBuffer, (size_t)ab.allocated);
+               bcopy(ab.base, (void *)attributeBuffer, (size_t)ulmin(bufferSize, ab.needed));
 
 out:
        if (vs.f_vol_name != NULL)
@@ -2183,6 +2226,18 @@ attr_pack_common_extended(struct vnode *vp, struct attrlist *alp,
                }
        }
 
+       if (alp->forkattr & ATTR_CMNEXT_LINKID) {
+               uint64_t linkid;
+
+               if (VATTR_IS_SUPPORTED(vap, va_linkid))
+                       linkid = vap->va_linkid;
+               else
+                       linkid = vap->va_fileid;
+
+               ATTR_PACK8((*abp), linkid);
+               abp->actual.forkattr |= ATTR_CMNEXT_LINKID;
+       }
+
        return 0;
 }
 
@@ -2730,7 +2785,7 @@ out:
 static int
 getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
     user_addr_t attributeBuffer, size_t bufferSize, uint64_t options,
-    enum uio_seg segflg, char* alt_name, struct ucred *file_cred)
+    enum uio_seg segflg, char* authoritative_name, struct ucred *file_cred)
 {
        struct vnode_attr va;
        kauth_action_t  action;
@@ -2746,6 +2801,9 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
        // must be true for fork attributes to be used as new common attributes
        const int use_fork = (options & FSOPT_ATTR_CMN_EXTENDED) != 0;
 
+       if (bufferSize < sizeof(uint32_t))
+               return (ERANGE);
+
        proc_is64 = proc_is64bit(vfs_context_proc(ctx));
 
        if (segflg == UIO_USERSPACE) {
@@ -2862,9 +2920,22 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
                                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: cannot allocate va_name buffer");
                                goto out;
                        }
+                       /*
+                        * If we have an authoritative_name, prefer that name.
+                        *
+                        * N.B. Since authoritative_name implies this is coming from getattrlistbulk,
+                        * we know the name is authoritative. For /dev/fd, we want to use the file
+                        * descriptor as the name not the underlying name of the associate vnode in a
+                        * particular file system.
+                        */
+                       if (authoritative_name) {
+                               /* Don't ask the file system */
+                               VATTR_CLEAR_ACTIVE(&va, va_name);
+                               strlcpy(va_name, authoritative_name, MAXPATHLEN);
+                       }
                }
 
-               va.va_name = va_name;
+               va.va_name = authoritative_name ? NULL : va_name;
 
                /*
                 * Call the filesystem.
@@ -2894,16 +2965,19 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
 #endif
 
                /* 
-                * If ATTR_CMN_NAME is not supported by filesystem and the
-                * caller has provided a name, use that.
+                * It we ask for the name, i.e., vname is non null and
+                * we have an authoritative name, then reset va_name is
+                * active and if needed set va_name is supported.
+                *
                 * A (buggy) filesystem may change fields which belong
                 * to us. We try to deal with that here as well.
                 */
                va.va_active = va_active;
-               if (alt_name  && va_name &&
-                   !(VATTR_IS_SUPPORTED(&va, va_name))) {
-                       strlcpy(va_name, alt_name, MAXPATHLEN);
-                       VATTR_SET_SUPPORTED(&va, va_name);
+               if (authoritative_name  && va_name) {
+                       VATTR_SET_ACTIVE(&va, va_name);
+                       if (!(VATTR_IS_SUPPORTED(&va, va_name))) {
+                               VATTR_SET_SUPPORTED(&va, va_name);
+                       }
                }
                va.va_name = va_name;
        }
@@ -3630,6 +3704,7 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
        struct fileproc *fp;
        struct fd_vn_data *fvdata;
        vfs_context_t ctx;
+       uthread_t ut;
        enum uio_seg segflg;
        int count;
        uio_t auio = NULL;
@@ -3649,6 +3724,7 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
        fvdata = NULL;
        eofflag = 0;
        ctx = vfs_context_current();
+       ut = get_bsdthread_info(current_thread());
        segflg = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32;
 
        if ((fp->f_fglob->fg_flag & FREAD) == 0) {
@@ -3667,11 +3743,6 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
        if (uap->options & FSOPT_LIST_SNAPSHOT) {
                vnode_t snapdvp;
 
-               if (!vfs_context_issuser(ctx)) {
-                       error = EPERM;
-                       goto out;
-               }
-
                if (!vnode_isvroot(dvp)) {
                        error = EINVAL;
                        goto out;
@@ -3800,8 +3871,14 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
                        (void)getattrlist_setupvattr_all(&al, &va, VNON, NULL,
                            IS_64BIT_PROCESS(p), (uap->options & FSOPT_ATTR_CMN_EXTENDED));
 
+                       /*
+                        * Set UT_KERN_RAGE_VNODES to cause all vnodes created by the
+                        * filesystem to be rapidly aged.
+                        */
+                       ut->uu_flag |= UT_KERN_RAGE_VNODES;
                        error = VNOP_GETATTRLISTBULK(dvp, &al, &va, auio, NULL,
                            options, &eofflag, &count, ctx);
+                       ut->uu_flag &= ~UT_KERN_RAGE_VNODES;
 
                        FREE(va_name, M_TEMP);
 
@@ -3822,8 +3899,10 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
                eofflag = 0;
                count = 0;
 
+               ut->uu_flag |= UT_KERN_RAGE_VNODES;
                error = readdirattr(dvp, fvdata, auio, &al, options,
                    &count, &eofflag, ctx);
+               ut->uu_flag &= ~UT_KERN_RAGE_VNODES;
        }
 
        if (count) {
@@ -3912,6 +3991,19 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
                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 */
+
        VFS_DEBUG(ctx, vp, "%p  ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
            vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
            (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
@@ -4098,6 +4190,10 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
                ATTR_UNPACK(va.va_guuid);
                VATTR_SET_ACTIVE(&va, va_guuid);
        }
+       if (al.commonattr & ATTR_CMN_ADDEDTIME) {
+               ATTR_UNPACK_TIME(va.va_addedtime, proc_is64);
+               VATTR_SET_ACTIVE(&va, va_addedtime);
+       }
        /* Support setattrlist of data protection class */
        if (al.commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) {
                ATTR_UNPACK(va.va_dataprotect_class);
@@ -4284,6 +4380,44 @@ out:
        return error;
 }
 
+int
+setattrlistat(proc_t p, struct setattrlistat_args *uap, __unused int32_t *retval)
+{
+       struct setattrlist_args ap;
+       struct vfs_context *ctx;
+       struct nameidata nd;
+       vnode_t vp = NULLVP;
+       uint32_t nameiflags;
+       int error;
+
+       ctx = vfs_context_current();
+
+       AUDIT_ARG(fd, uap->fd);
+       /*
+        * Look up the file.
+        */
+       nameiflags = AUDITVNPATH1;
+       if (!(uap->options & FSOPT_NOFOLLOW))
+               nameiflags |= FOLLOW;
+       NDINIT(&nd, LOOKUP, OP_SETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
+       if ((error = nameiat(&nd, uap->fd)) != 0)
+               goto out;
+       vp = nd.ni_vp;
+       nameidone(&nd);
+
+       ap.path = 0;
+       ap.alist = uap->alist;
+       ap.attributeBuffer = uap->attributeBuffer;
+       ap.bufferSize = uap->bufferSize;
+       ap.options = uap->options;
+
+       error = setattrlist_internal(vp, &ap, p, ctx);
+out:
+       if (vp)
+               vnode_put(vp);
+       return (error);
+}
+
 int
 fsetattrlist(proc_t p, struct fsetattrlist_args *uap, __unused int32_t *retval)
 {