]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_attrlist.c
xnu-4570.20.62.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_attrlist.c
index 38d7a3f2453d63833981b85e0de6266694f0cfd6..4aab3ac014479b51710142c9a6b0f5ffa24b140f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995-2014 Apple Inc. All rights reserved.
+ * Copyright (c) 1995-2017 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -51,7 +51,7 @@
 #include <sys/fsevents.h>
 #include <kern/kalloc.h>
 #include <miscfs/specfs/specdev.h>
-#include <hfs/hfs.h>
+#include <security/audit/audit.h>
 
 #if CONFIG_MACF
 #include <security/mac_framework.h>
@@ -270,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)
@@ -357,6 +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) | 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}
@@ -364,7 +366,7 @@ static struct getvolattrlist_attrtab getvolattrlist_vol_tab[] = {
 
 static int
 getvolattrlist_parsetab(struct getvolattrlist_attrtab *tab, attrgroup_t attrs, struct vfs_attr *vsp,
-    ssize_t *sizep, int is_64bit)
+               ssize_t *sizep, int is_64bit, unsigned int maxiter)
 {
        attrgroup_t     recognised;
 
@@ -384,7 +386,7 @@ getvolattrlist_parsetab(struct getvolattrlist_attrtab *tab, attrgroup_t attrs, s
                                *sizep += tab->size;
                        }
                }
-       } while ((++tab)->attr != 0);
+       } while (((++tab)->attr != 0) && (--maxiter > 0));
        
        /* check to make sure that we recognised all of the passed-in attributes */
        if (attrs & ~recognised)
@@ -400,6 +402,8 @@ static int
 getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t *sizep, int is_64bit)
 {
        int     error;
+       if (!alp)
+               return EINVAL;
 
        /*
         * Parse the above tables.
@@ -411,13 +415,14 @@ getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t
                        return (EINVAL);
                }
                if ((error = getvolattrlist_parsetab(getvolattrlist_common_tab,
-                                                   alp->commonattr, vsp, sizep,
-                                                   is_64bit)) != 0) {
+                               alp->commonattr, vsp, sizep,
+                               is_64bit,
+                               sizeof(getvolattrlist_common_tab)/sizeof(getvolattrlist_common_tab[0]))) != 0) {
                        return(error);
                }
        }
        if (alp->volattr &&
-           (error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit)) != 0)
+           (error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit, sizeof(getvolattrlist_vol_tab)/sizeof(getvolattrlist_vol_tab[0]))) != 0)
                return(error);
 
        return(0);
@@ -510,6 +515,9 @@ static struct getattrlist_attrtab getattrlist_dir_tab[] = {
        {ATTR_DIR_LINKCOUNT,    VATTR_BIT(va_dirlinkcount),     sizeof(uint32_t),               KAUTH_VNODE_READ_ATTRIBUTES},
        {ATTR_DIR_ENTRYCOUNT,   VATTR_BIT(va_nchildren),        sizeof(uint32_t),               KAUTH_VNODE_READ_ATTRIBUTES},
        {ATTR_DIR_MOUNTSTATUS,  0,                              sizeof(uint32_t),               KAUTH_VNODE_READ_ATTRIBUTES},
+       {ATTR_DIR_ALLOCSIZE,    VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
+       {ATTR_DIR_IOBLOCKSIZE,  VATTR_BIT(va_iosize),           sizeof(uint32_t),               KAUTH_VNODE_READ_ATTRIBUTES},
+       {ATTR_DIR_DATALENGTH,   VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
        {0, 0, 0, 0}
 };
 static struct getattrlist_attrtab getattrlist_file_tab[] = {
@@ -523,7 +531,15 @@ static struct getattrlist_attrtab getattrlist_file_tab[] = {
        {ATTR_FILE_RSRCLENGTH,  0,                              sizeof(off_t),                  KAUTH_VNODE_READ_ATTRIBUTES},
        {ATTR_FILE_RSRCALLOCSIZE, 0,                            sizeof(off_t),                  KAUTH_VNODE_READ_ATTRIBUTES},
        {0, 0, 0, 0}
-};     
+};
+
+//for forkattr bits repurposed as new common attributes
+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}
+};
 
 /*
  * This table is for attributes which are only set from the getattrlistbulk(2)
@@ -550,13 +566,19 @@ static struct getattrlist_attrtab getattrlistbulk_file_tab[] = {
        {0, 0, 0, 0}
 };
 
+static struct getattrlist_attrtab getattrlistbulk_common_extended_tab[] = {
+       /* getattrlist_parsetab() expects > 1 entries */
+       {0, 0, 0, 0},
+       {0, 0, 0, 0}
+};
+
 /*
  * The following are attributes that VFS can derive.
  *
  * A majority of them are the same attributes that are required for stat(2) and statfs(2).
  */
 #define VFS_DFLT_ATTR_VOL      (ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE |  \
-                                ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE |  \
+                                ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE |  ATTR_VOL_QUOTA_SIZE | ATTR_VOL_RESERVED_SIZE | \
                                 ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION |  \
                                 ATTR_VOL_ALLOCATIONCLUMP |  ATTR_VOL_IOBLOCKSIZE |  \
                                 ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS |  \
@@ -576,8 +598,7 @@ static struct getattrlist_attrtab getattrlistbulk_file_tab[] = {
                                 ATTR_CMN_DOCUMENT_ID | ATTR_CMN_GEN_COUNT | \
                                 ATTR_CMN_DATA_PROTECT_FLAGS)
 
-#define VFS_DFLT_ATT_CMN_EXT   (ATTR_CMN_EXT_GEN_COUNT | ATTR_CMN_EXT_DOCUMENT_ID |\
-                                ATTR_CMN_EXT_DATA_PROTECT_FLAGS)
+#define VFS_DFLT_ATTR_CMN_EXT  (ATTR_CMNEXT_PRIVATESIZE | ATTR_CMNEXT_LINKID)
 
 #define VFS_DFLT_ATTR_DIR      (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
 
@@ -590,11 +611,13 @@ static struct getattrlist_attrtab getattrlistbulk_file_tab[] = {
 static int
 getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs,
     struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp,
-    int is_64bit)
+    int is_64bit, unsigned int maxiter)
 {
        attrgroup_t     recognised;
-
        recognised = 0;
+       if (!tab)
+               return EINVAL;
+
        do {
                /* is this attribute set? */
                if (tab->attr & attrs) {
@@ -619,7 +642,7 @@ getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs,
                        if (attrs == recognised)
                                break;  /* all done, get out */
                }
-       } while ((++tab)->attr != 0);
+       } while (((++tab)->attr != 0) && (--maxiter > 0));
        
        /* check to make sure that we recognised all of the passed-in attributes */
        if (attrs & ~recognised)
@@ -632,7 +655,7 @@ getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs,
  * the data from a filesystem.
  */
 static int
-getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, int is_64bit, int isdir)
+getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, int is_64bit, int isdir, int use_fork)
 {
        int     error;
 
@@ -642,13 +665,16 @@ getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *si
        *sizep = sizeof(uint32_t);      /* length count */
        *actionp = 0;
        if (alp->commonattr &&
-           (error = getattrlist_parsetab(getattrlist_common_tab, alp->commonattr, vap, sizep, actionp, is_64bit)) != 0)
+           (error = getattrlist_parsetab(getattrlist_common_tab, alp->commonattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_common_tab)/sizeof(getattrlist_common_tab[0]))) != 0)
                return(error);
        if (isdir && alp->dirattr &&
-           (error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr, vap, sizep, actionp, is_64bit)) != 0)
+           (error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_dir_tab)/sizeof(getattrlist_dir_tab[0]))) != 0)
                return(error);
        if (!isdir && alp->fileattr &&
-           (error = getattrlist_parsetab(getattrlist_file_tab, alp->fileattr, vap, sizep, actionp, is_64bit)) != 0)
+           (error = getattrlist_parsetab(getattrlist_file_tab, alp->fileattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_file_tab)/sizeof(getattrlist_file_tab[0]))) != 0)
+               return(error);
+       if (use_fork && alp->forkattr &&
+           (error = getattrlist_parsetab(getattrlist_common_extended_tab, alp->forkattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_common_extended_tab)/sizeof(getattrlist_common_extended_tab[0]))) != 0)
                return(error);
 
        return(0);
@@ -660,7 +686,7 @@ getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *si
  */
 static int
 getattrlist_setupvattr_all(struct attrlist *alp, struct vnode_attr *vap,
-    enum vtype obj_type, ssize_t *fixedsize, int is_64bit)
+    enum vtype obj_type, ssize_t *fixedsize, int is_64bit, int use_fork)
 {
        int     error = 0;
 
@@ -672,12 +698,14 @@ getattrlist_setupvattr_all(struct attrlist *alp, struct vnode_attr *vap,
        }
        if (alp->commonattr) {
                error = getattrlist_parsetab(getattrlist_common_tab,
-                   alp->commonattr, vap, fixedsize, NULL, is_64bit);
+                       alp->commonattr, vap, fixedsize, NULL, is_64bit,
+                       sizeof(getattrlist_common_tab)/sizeof(getattrlist_common_tab[0]));
 
                if (!error) {
                        /* Ignore any errrors from the bulk table */
                        (void)getattrlist_parsetab(getattrlistbulk_common_tab,
-                           alp->commonattr, vap, fixedsize, NULL, is_64bit);
+                               alp->commonattr, vap, fixedsize, NULL, is_64bit,
+                               sizeof(getattrlistbulk_common_tab)/sizeof(getattrlistbulk_common_tab[0]));
                        /*
                         * turn off va_fsid since we will be using only
                         * va_fsid64 for ATTR_CMN_FSID.
@@ -688,17 +716,33 @@ getattrlist_setupvattr_all(struct attrlist *alp, struct vnode_attr *vap,
 
        if (!error && (obj_type == VNON || obj_type == VDIR) && alp->dirattr) {
                error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr,
-                   vap, fixedsize, NULL, is_64bit);
+                               vap, fixedsize, NULL, is_64bit,
+                               sizeof(getattrlist_dir_tab)/sizeof(getattrlist_dir_tab[0]));
        }
 
        if (!error && (obj_type != VDIR) && alp->fileattr) {
                error = getattrlist_parsetab(getattrlist_file_tab,
-                   alp->fileattr, vap, fixedsize, NULL, is_64bit);
+                       alp->fileattr, vap, fixedsize, NULL, is_64bit,
+                       sizeof(getattrlist_file_tab)/sizeof(getattrlist_file_tab[0]));
 
                if (!error) {
                        /*Ignore any errors from the bulk table */
                        (void)getattrlist_parsetab(getattrlistbulk_file_tab,
-                           alp->fileattr, vap, fixedsize, NULL, is_64bit);
+                               alp->fileattr, vap, fixedsize, NULL, is_64bit,
+                               sizeof(getattrlistbulk_file_tab)/sizeof(getattrlistbulk_file_tab[0]));
+               }
+       }
+
+       /* fork attributes are like extended common attributes if enabled*/
+       if (!error && use_fork && alp->forkattr) {
+               error = getattrlist_parsetab(getattrlist_common_extended_tab,
+                       alp->forkattr, vap, fixedsize, NULL, is_64bit,
+                       sizeof(getattrlist_common_extended_tab)/sizeof(getattrlist_common_extended_tab[0]));
+
+               if (!error) {
+                       (void)getattrlist_parsetab(getattrlistbulk_common_extended_tab,
+                               alp->forkattr, vap, fixedsize, NULL, is_64bit,
+                               sizeof(getattrlistbulk_common_extended_tab)/sizeof(getattrlistbulk_common_extended_tab[0]));
                }
        }
 
@@ -709,8 +753,10 @@ int
 vfs_setup_vattr_from_attrlist(struct attrlist *alp, struct vnode_attr *vap,
     enum vtype obj_vtype, ssize_t *attrs_fixed_sizep, vfs_context_t ctx)
 {
+       // the caller passes us no options, we assume the caller wants the new fork
+       // attr behavior, hence the hardcoded 1
        return (getattrlist_setupvattr_all(alp, vap, obj_vtype,
-           attrs_fixed_sizep, IS_64BIT_PROCESS(vfs_context_proc(ctx))));
+           attrs_fixed_sizep, IS_64BIT_PROCESS(vfs_context_proc(ctx)), 1));
 }
 
 
@@ -722,7 +768,7 @@ vfs_setup_vattr_from_attrlist(struct attrlist *alp, struct vnode_attr *vap,
  * missing attributes from the file system
  */
 static void
-getattrlist_fixupattrs(attribute_set_t *asp, struct vnode_attr *vap)
+getattrlist_fixupattrs(attribute_set_t *asp, struct vnode_attr *vap, int use_fork)
 {
        struct getattrlist_attrtab *tab;
 
@@ -776,6 +822,16 @@ getattrlist_fixupattrs(attribute_set_t *asp, struct vnode_attr *vap)
                        }
                } while ((++tab)->attr != 0);
        }
+       if (use_fork && asp->forkattr) {
+               tab = getattrlist_common_extended_tab;
+               do {
+                       if ((tab->attr & asp->forkattr) &&
+                           (tab->bits & vap->va_active) &&
+                           (vap->va_supported & tab->bits) == 0) {
+                               asp->forkattr &= ~tab->attr;
+                       }
+               } while ((++tab)->attr != 0);
+       }
 }
 
 static int
@@ -908,17 +964,18 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                        }
                }
 
-#if CONFIG_MACF
-               error = mac_mount_check_getattr(ctx, mnt, &vs);
-               if (error != 0)
-                       goto out;
-#endif
                VFS_DEBUG(ctx, vp, "ATTRLIST -       calling to get %016llx with supported %016llx", vs.f_active, vs.f_supported);
                if ((error = vfs_getattr(mnt, &vs, ctx)) != 0) {
                        VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
                        goto out;
                }
-
+#if CONFIG_MACF
+               error = mac_mount_check_getattr(ctx, mnt, &vs);
+               if (error != 0) {
+                       VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d", error);
+                       goto out;
+               }
+#endif
                /*
                 * Did we ask for something the filesystem doesn't support?
                 */
@@ -951,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;
@@ -1018,7 +1075,13 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                        VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp);
                        goto out;
                }
-
+#if CONFIG_MACF
+               error = mac_vnode_check_getattr(ctx, NOCRED, vp, &va);
+               if (error != 0) {
+                       VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d for root vnode", error);
+                       goto out;
+               }
+#endif
                if (VATTR_IS_ACTIVE(&va, va_encoding) &&
                    !VATTR_IS_SUPPORTED(&va, va_encoding)) {
                        if (!return_valid || pack_invalid)
@@ -1167,6 +1230,7 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                 * This attribute isn't really Finder Info, at least for HFS.
                 */
                if (vp->v_tag == VT_HFS) {
+#define HFS_GET_BOOT_INFO   (FCNTL_FS_SPECIFIC_BASE + 0x00004)
                        error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, (caddr_t)&f, 0, ctx);
                        if (error == 0) {
                                attrlist_pack_fixed(&ab, f, sizeof(f));
@@ -1331,6 +1395,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;
        }
@@ -1338,6 +1417,14 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                ATTR_PACK(&ab, vs.f_uuid);
                ab.actual.volattr |= ATTR_VOL_UUID;
        }
+       if (alp->volattr & ATTR_VOL_QUOTA_SIZE) {
+               ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_quota);
+               ab.actual.volattr |= ATTR_VOL_QUOTA_SIZE;
+       }
+       if (alp->volattr & ATTR_VOL_RESERVED_SIZE) {
+               ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_reserved);
+               ab.actual.volattr |= ATTR_VOL_RESERVED_SIZE;
+       }
        if (alp->volattr & ATTR_VOL_ATTRIBUTES) {
                /* fix up volume attribute information */
 
@@ -1475,45 +1562,64 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                }
        }
        if (alp->commonattr & ATTR_CMN_OBJID) {
-               fsobj_id_t f;
                /*
                 * Carbon can't deal with us reporting the target ID
                 * for links.  So we ask the filesystem to give us the
                 * source ID as well, and if it gives us one, we use
                 * it instead.
                 */
-               if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
-                       f.fid_objno = vap->va_linkid;
+               if (vap->va_vaflags & VA_64BITOBJIDS) {
+                       if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
+                               ATTR_PACK8((*abp),  vap->va_linkid);
+                       } else {
+                               ATTR_PACK8((*abp),  vap->va_fileid);
+                       }
                } else {
-                       f.fid_objno = vap->va_fileid;
+                       fsobj_id_t f;
+                       if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
+                               f.fid_objno = (uint32_t)vap->va_linkid;
+                       } else {
+                               f.fid_objno = (uint32_t)vap->va_fileid;
+                       }
+                       f.fid_generation = 0;
+                       ATTR_PACK8((*abp), f);
                }
-               f.fid_generation = 0;
-               ATTR_PACK8((*abp), f);
                abp->actual.commonattr |= ATTR_CMN_OBJID;
        }
        if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
-               fsobj_id_t f;
                /*
                 * Carbon can't deal with us reporting the target ID
                 * for links.  So we ask the filesystem to give us the
                 * source ID as well, and if it gives us one, we use
                 * it instead.
                 */
-               if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
-                       f.fid_objno = vap->va_linkid;
+               if (vap->va_vaflags & VA_64BITOBJIDS) {
+                       if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
+                               ATTR_PACK8((*abp),  vap->va_linkid);
+                       } else {
+                               ATTR_PACK8((*abp),  vap->va_fileid);
+                       }
                } else {
-                       f.fid_objno = vap->va_fileid;
+                       fsobj_id_t f;
+                       if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
+                               f.fid_objno = (uint32_t)vap->va_linkid;
+                       } else {
+                               f.fid_objno = (uint32_t)vap->va_fileid;
+                       }
+                       f.fid_generation = 0;
+                       ATTR_PACK8((*abp), f);
                }
-               f.fid_generation = 0;
-               ATTR_PACK8((*abp), f);
                abp->actual.commonattr |= ATTR_CMN_OBJPERMANENTID;
        }
        if (alp->commonattr & ATTR_CMN_PAROBJID) {
-               fsobj_id_t f;
-
-               f.fid_objno = vap->va_parentid;  /* could be lossy here! */
-               f.fid_generation = 0;
-               ATTR_PACK8((*abp), f);
+               if (vap->va_vaflags & VA_64BITOBJIDS) {
+                       ATTR_PACK8((*abp), vap->va_parentid);
+               } else {
+                       fsobj_id_t f;
+                       f.fid_objno = (uint32_t)vap->va_parentid;
+                       f.fid_generation = 0;
+                       ATTR_PACK8((*abp), f);
+               }
                abp->actual.commonattr |= ATTR_CMN_PAROBJID;
        }
        if (alp->commonattr & ATTR_CMN_SCRIPT) {
@@ -1741,8 +1847,10 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
        }
        
        if (alp->commonattr & ATTR_CMN_FULLPATH) {
-               attrlist_pack_string (abp, fullpathptr, fullpathlen);
-               abp->actual.commonattr |= ATTR_CMN_FULLPATH;
+               if (vp) {
+                       attrlist_pack_string (abp, fullpathptr, fullpathlen);
+                       abp->actual.commonattr |= ATTR_CMN_FULLPATH;
+               }
        }
     
        if (alp->commonattr & ATTR_CMN_ADDEDTIME) {
@@ -1769,7 +1877,7 @@ out:
 
 static errno_t
 attr_pack_dir(struct vnode *vp, struct attrlist *alp, struct _attrlist_buf *abp,
-    struct vnode_attr *vap)
+    struct vnode_attr *vap, int return_valid, int pack_invalid)
 {
        if (alp->dirattr & ATTR_DIR_LINKCOUNT) {  /* full count of entries */
                ATTR_PACK4((*abp), (uint32_t)vap->va_dirlinkcount);
@@ -1811,6 +1919,43 @@ attr_pack_dir(struct vnode *vp, struct attrlist *alp, struct _attrlist_buf *abp,
                ATTR_PACK4((*abp), mntstat);
                abp->actual.dirattr |= ATTR_DIR_MOUNTSTATUS;
        }
+       if (alp->dirattr & ATTR_DIR_ALLOCSIZE) {
+               if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
+                       ATTR_PACK8((*abp), vap->va_data_alloc);
+                       abp->actual.dirattr |= ATTR_DIR_ALLOCSIZE;
+               } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
+                       ATTR_PACK8((*abp), vap->va_total_alloc);
+                       abp->actual.dirattr |= ATTR_DIR_ALLOCSIZE;
+               } else if (!return_valid || pack_invalid) {
+                       uint64_t zero_val = 0;
+                       ATTR_PACK8((*abp), zero_val);
+               }
+       }
+       if (alp->dirattr & ATTR_DIR_IOBLOCKSIZE) {
+               if (VATTR_IS_SUPPORTED(vap, va_iosize)) {
+                       ATTR_PACK4((*abp), vap->va_iosize);
+                       abp->actual.dirattr |= ATTR_DIR_IOBLOCKSIZE;
+               } else if (!return_valid || pack_invalid) {
+                       ATTR_PACK4((*abp), 0);
+               }
+       }
+       /*
+        * If the filesystem does not support datalength
+        * or dataallocsize, then we infer that totalsize and
+        * totalalloc are substitutes.
+        */
+       if (alp->dirattr & ATTR_DIR_DATALENGTH) {
+               if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
+                       ATTR_PACK8((*abp), vap->va_data_size);
+                       abp->actual.dirattr |= ATTR_DIR_DATALENGTH;
+               } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
+                       ATTR_PACK8((*abp), vap->va_total_size);
+                       abp->actual.dirattr |= ATTR_DIR_DATALENGTH;
+               } else if (!return_valid || pack_invalid) {
+                       uint64_t zero_val = 0;
+                       ATTR_PACK8((*abp), zero_val);
+               }
+       }
 
        return 0;
 }
@@ -1891,7 +2036,7 @@ attr_pack_file(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
 
                        if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
                                totalsize += vap->va_data_size;
-                       } else {
+                       } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
                                totalsize += vap->va_total_size;
                        }
 
@@ -1916,7 +2061,7 @@ attr_pack_file(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                         */
                        if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
                                totalalloc += vap->va_data_alloc;
-                       } else {
+                       } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
                                totalalloc += vap->va_total_alloc;
                        }
 
@@ -1932,8 +2077,12 @@ attr_pack_file(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                }
        }
        if (alp->fileattr & ATTR_FILE_IOBLOCKSIZE) {
-               ATTR_PACK4((*abp), vap->va_iosize);
-               abp->actual.fileattr |= ATTR_FILE_IOBLOCKSIZE;
+               if (VATTR_IS_SUPPORTED(vap, va_iosize)) {
+                       ATTR_PACK4((*abp), vap->va_iosize);
+                       abp->actual.fileattr |= ATTR_FILE_IOBLOCKSIZE;
+               } else if (!return_valid || pack_invalid) {
+                       ATTR_PACK4((*abp), 0);
+               }
        }
        if (alp->fileattr & ATTR_FILE_CLUMPSIZE) {
                if (!return_valid || pack_invalid) {
@@ -1972,18 +2121,26 @@ attr_pack_file(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
        if (alp->fileattr & ATTR_FILE_DATALENGTH) {
                if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
                        ATTR_PACK8((*abp), vap->va_data_size);
-               } else {
+                       abp->actual.fileattr |= ATTR_FILE_DATALENGTH;
+               } else if (VATTR_IS_SUPPORTED(vap, va_total_size)){
                        ATTR_PACK8((*abp), vap->va_total_size);
+                       abp->actual.fileattr |= ATTR_FILE_DATALENGTH;
+               } else if (!return_valid || pack_invalid) {
+                       uint64_t zero_val = 0;
+                       ATTR_PACK8((*abp), zero_val);
                }
-               abp->actual.fileattr |= ATTR_FILE_DATALENGTH;
        }
        if (alp->fileattr & ATTR_FILE_DATAALLOCSIZE) {
                if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
                        ATTR_PACK8((*abp), vap->va_data_alloc);
-               } else {
+                       abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
+               } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)){
                        ATTR_PACK8((*abp), vap->va_total_alloc);
+                       abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
+               } else if (!return_valid || pack_invalid) {
+                       uint64_t zero_val = 0;
+                       ATTR_PACK8((*abp), zero_val);
                }
-               abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
        }
        /* already got the resource fork size/allocation above */
        if (alp->fileattr & ATTR_FILE_RSRCLENGTH) {
@@ -2016,6 +2173,48 @@ out:
        return (error);
 }
 
+/*
+ * Pack FORKATTR attributes into a user buffer.
+ * alp is a pointer to the bitmap of attributes required.
+ * abp is the state of the attribute filling operation.
+ * The attribute data (along with some other fields that are required
+ * are in ad.
+ */
+static errno_t
+attr_pack_common_extended(struct vnode *vp, struct attrlist *alp,
+               struct _attrlist_buf *abp, const char *relpathptr, ssize_t relpathlen,
+               struct vnode_attr *vap, int return_valid, int pack_invalid)
+{
+       if (vp && (alp->forkattr & ATTR_CMNEXT_RELPATH)) {
+               attrlist_pack_string(abp, relpathptr, relpathlen);
+               abp->actual.forkattr |= ATTR_CMNEXT_RELPATH;
+       }
+
+       if (alp->forkattr & ATTR_CMNEXT_PRIVATESIZE) {
+               if (VATTR_IS_SUPPORTED(vap, va_private_size)) {
+                       ATTR_PACK8((*abp), vap->va_private_size);
+                       abp->actual.forkattr |= ATTR_CMNEXT_PRIVATESIZE;
+               } else if (!return_valid || pack_invalid) {
+                       uint64_t zero_val = 0;
+                       ATTR_PACK8((*abp), zero_val);
+               }
+       }
+
+       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;
+}
+
 static void
 vattr_get_alt_data(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
     int return_valid, int is_bulk, vfs_context_t ctx)
@@ -2093,7 +2292,8 @@ vattr_get_alt_data(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
 static errno_t
 calc_varsize(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
    ssize_t *varsizep, char *fullpathptr, ssize_t *fullpathlenp,
-   const char **vnamep, const char **cnpp, ssize_t *cnlp)  
+   char *relpathptr, ssize_t *relpathlenp, const char **vnamep,
+   const char **cnpp, ssize_t *cnlp)
 {
        int error = 0;
 
@@ -2160,6 +2360,25 @@ calc_varsize(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
                *varsizep += roundup(((*fullpathlenp) + 1), 4);
        }
 
+       /*
+        * Compute this vnode's volume relative path.
+        */
+       if (vp && (alp->forkattr & ATTR_CMNEXT_RELPATH)) {
+               int len;
+               int err;
+
+               /* call build_path making sure NOT to use the cache-only behavior */
+               err = build_path(vp, relpathptr, MAXPATHLEN, &len, BUILDPATH_VOLUME_RELATIVE, vfs_context_current());
+               if (err) {
+                       error = err;
+                       goto out;
+               }
+
+               //`len' includes trailing null
+               *relpathlenp = len - 1;
+               *varsizep += roundup(len, 4);
+       }
+
        /*
         * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
         *
@@ -2200,11 +2419,14 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
        ssize_t cnl;
        char *fullpathptr;
        ssize_t fullpathlen;
+       char *relpathptr;
+       ssize_t relpathlen;
        int error;
        int proc_is64;
        int return_valid;
        int pack_invalid;
        int alloc_local_buf;
+       const int use_fork = options & FSOPT_ATTR_CMN_EXTENDED;
 
        proc_is64 = proc_is64bit(vfs_context_proc(ctx));
        ab.base = NULL;
@@ -2212,6 +2434,8 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
        cnl = 0;
        fullpathptr = NULL;
        fullpathlen = 0;
+       relpathptr = NULL;
+       relpathlen = 0;
        error = 0;
        alloc_local_buf = 0;
 
@@ -2238,14 +2462,14 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
                if (!VATTR_ALL_SUPPORTED(vap)) {
                        if (return_valid && pack_invalid) {
                                /* Fix up valid mask for post processing */
-                               getattrlist_fixupattrs(&ab.valid, vap);
+                               getattrlist_fixupattrs(&ab.valid, vap, use_fork);
                                        
                                /* Force packing of everything asked for */
                                vap->va_supported = vap->va_active;
                        } else if (return_valid) {
                                /* Adjust the requested attributes */
                                getattrlist_fixupattrs(
-                                   (attribute_set_t *)&(alp->commonattr), vap);
+                                   (attribute_set_t *)&(alp->commonattr), vap, use_fork);
                        } else {
                                error = EINVAL;
                        }
@@ -2255,20 +2479,33 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
                        goto out;
        }
 
-       if (alp->commonattr & (ATTR_CMN_FULLPATH)) {
+       //if a path is requested, allocate a temporary buffer to build it
+       if (vp && (alp->commonattr & (ATTR_CMN_FULLPATH))) {
                fullpathptr = (char*) kalloc(MAXPATHLEN);
                if (fullpathptr == NULL) {
                        error = ENOMEM;
                        VFS_DEBUG(ctx,vp, "ATTRLIST - ERROR: cannot allocate fullpath buffer");
                        goto out;
                }
+               bzero(fullpathptr, MAXPATHLEN);
+       }
+
+       // only interpret fork attributes if they're used as new common attributes
+       if (vp && use_fork && (alp->forkattr & (ATTR_CMNEXT_RELPATH))) {
+               relpathptr = (char*) kalloc(MAXPATHLEN);
+               if (relpathptr == NULL) {
+                       error = ENOMEM;
+                       VFS_DEBUG(ctx,vp, "ATTRLIST - ERROR: cannot allocate relpath buffer");
+                       goto out;
+               }
+               bzero(relpathptr, MAXPATHLEN);
        }
 
        /*
         * Compute variable-space requirements.
         */
        error = calc_varsize(vp, alp, vap, &varsize, fullpathptr, &fullpathlen,
-           &vname, &cnp, &cnl);
+                       relpathptr, &relpathlen, &vname, &cnp, &cnl);
        if (error)
                goto out;
 
@@ -2386,11 +2623,11 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
 
        /* common attributes ************************************************/
        error = attr_pack_common(ctx, vp, alp, &ab, vap, proc_is64, cnp, cnl,
-           fullpathptr, fullpathlen, return_valid, pack_invalid, vtype, is_bulk); 
+           fullpathptr, fullpathlen, return_valid, pack_invalid, vtype, is_bulk);
 
        /* directory attributes *********************************************/
        if (!error && alp->dirattr && (vtype == VDIR)) {
-               error = attr_pack_dir(vp, alp, &ab, vap);
+               error = attr_pack_dir(vp, alp, &ab, vap, return_valid, pack_invalid);
        }
 
        /* file attributes **************************************************/
@@ -2399,6 +2636,12 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
                    pack_invalid, is_bulk);
        }
 
+       /* common extended attributes *****************************************/
+       if (!error && use_fork) {
+               error = attr_pack_common_extended(vp, alp, &ab, relpathptr, relpathlen,
+                   vap, return_valid, pack_invalid);
+       }
+
        if (error)
                goto out;
        
@@ -2458,6 +2701,8 @@ out:
                vnode_putname(vname);
        if (fullpathptr)
                kfree(fullpathptr, MAXPATHLEN);
+       if (relpathptr)
+               kfree(relpathptr, MAXPATHLEN);
        if (ab.base != NULL && alloc_local_buf)
                FREE(ab.base, M_TEMP);
        return (error);
@@ -2483,7 +2728,7 @@ vfs_attr_pack(vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options,
        vap->va_active = 0;
 
        error = getattrlist_setupvattr_all(alp, vap, v_type, &fixedsize,
-           proc_is64bit(vfs_context_proc(ctx)));
+           proc_is64bit(vfs_context_proc(ctx)), options & FSOPT_ATTR_CMN_EXTENDED);
 
        if (error) {
                VFS_DEBUG(ctx, vp,
@@ -2514,7 +2759,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)
+    enum uio_seg segflg, char* authoritative_name, struct ucred *file_cred)
 {
        struct vnode_attr va;
        kauth_action_t  action;
@@ -2527,6 +2772,8 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
        int             vtype = 0;
        uio_t           auio;
        char uio_buf[ UIO_SIZEOF(1)];
+       // must be true for fork attributes to be used as new common attributes
+       const int use_fork = (options & FSOPT_ATTR_CMN_EXTENDED) != 0;
 
        proc_is64 = proc_is64bit(vfs_context_proc(ctx));
 
@@ -2559,13 +2806,15 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
 #endif /* MAC */
 
        /*
-        * It is legal to request volume or file attributes,
-        * but not both.
+        * It is legal to request volume or file attributes, but not both.
+        *
+        * 26903449 fork attributes can also be requested, but only if they're
+        * interpreted as new, common attributes
         */
        if (alp->volattr) {
-               if (alp->fileattr || alp->dirattr || alp->forkattr) {
+               if (alp->fileattr || alp->dirattr || (alp->forkattr && !use_fork)) {
                        error = EINVAL;
-                       VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: mixed volume/file/directory/fork attributes");
+                       VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: mixed volume/file/directory attributes");
                        goto out;
                }
                /* handle volume attribute request */
@@ -2585,12 +2834,24 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
                goto out;
        }
 
+       /* common extended attributes require FSOPT_ATTR_CMN_EXTENDED option */
+       if (!(use_fork) && (alp->forkattr & ATTR_CMNEXT_VALIDMASK)) {
+               error = EINVAL;
+               goto out;
+       }
+
+       /* FSOPT_ATTR_CMN_EXTENDED requires forkattrs are not referenced */
+       if ((options & FSOPT_ATTR_CMN_EXTENDED) && (alp->forkattr & (ATTR_FORK_VALIDMASK))) {
+               error = EINVAL;
+               goto out;
+       }
+
        /* Check for special packing semantics */
        return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) ? 1 : 0;
        pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS) ? 1 : 0;
        if (pack_invalid) {
                /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
-               if (!return_valid || alp->forkattr) {
+               if (!return_valid || (alp->forkattr && !use_fork)) {
                        error = EINVAL;
                        goto out;
                }
@@ -2606,7 +2867,7 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
        /*
         * Set up the vnode_attr structure and authorise.
         */
-       if ((error = getattrlist_setupvattr(alp, &va, &fixedsize, &action, proc_is64, (vtype == VDIR))) != 0) {
+       if ((error = getattrlist_setupvattr(alp, &va, &fixedsize, &action, proc_is64, (vtype == VDIR), use_fork)) != 0) {
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
                goto out;
        }
@@ -2616,7 +2877,6 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
        }
 
 
-
        if (va.va_active != 0) {
                uint64_t va_active = va.va_active;
 
@@ -2631,9 +2891,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.
@@ -2642,19 +2915,40 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
                        VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
                        goto out;
                }
-
+#if CONFIG_MACF
+               /*
+                * Give MAC polices a chance to reject or filter the
+                * attributes returned by the filesystem.  Note that MAC
+                * policies are consulted *after* calling the filesystem
+                * because filesystems can return more attributes than
+                * were requested so policies wouldn't be authoritative
+                * is consulted beforehand.  This also gives policies an
+                * opportunity to change the values of attributes
+                * retrieved.
+                */
+               error = mac_vnode_check_getattr(ctx, file_cred, vp, &va);
+               if (error) {
+                       VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d", error);
+                       goto out;
+               }
+#else
+               (void)file_cred;
+#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;
        }
@@ -2679,17 +2973,19 @@ fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval)
        vnode_t vp;
        int error;
        struct attrlist al;
+       struct fileproc *fp;
 
        ctx = vfs_context_current();
+       vp = NULL;
+       fp = NULL;
        error = 0;
 
        if ((error = file_vnode(uap->fd, &vp)) != 0)
                return (error);
 
-       if ((error = vnode_getwithref(vp)) != 0) {
-               file_drop(uap->fd);
-               return(error);
-       }
+       if ((error = fp_lookup(p, uap->fd, &fp, 0)) != 0 ||
+           (error = vnode_getwithref(vp)) != 0)
+               goto out;
 
        /*
         * Fetch the attribute request.
@@ -2702,12 +2998,15 @@ fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval)
        error = getattrlist_internal(ctx, vp, &al, uap->attributeBuffer,
                                     uap->bufferSize, uap->options,
                                     (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : \
-                                    UIO_USERSPACE32), NULL);
+                                    UIO_USERSPACE32), NULL,
+                                    fp->f_fglob->fg_cred);
 
 out:
-       file_drop(uap->fd);
+       if (fp)
+               fp_drop(p, uap->fd, fp, 0);
        if (vp)
                vnode_put(vp);
+       file_drop(uap->fd);
 
        return error;
 }
@@ -2741,7 +3040,7 @@ getattrlistat_internal(vfs_context_t ctx, user_addr_t path,
        vp = nd.ni_vp;
 
        error = getattrlist_internal(ctx, vp, alp, attributeBuffer,
-           bufferSize, options, segflg, NULL);
+           bufferSize, options, segflg, NULL, NOCRED);
        
        /* Retain the namei reference until the getattrlist completes. */
        nameidone(&nd);
@@ -3063,7 +3362,7 @@ get_error_attributes(vnode_t vp, struct attrlist *alp, uint64_t options,
        fsiz = 0;
        (void)getattrlist_setupvattr(&al, NULL, (ssize_t *)&fsiz,
            &action, proc_is64bit(vfs_context_proc(ctx)),
-           (vnode_vtype(vp) == VDIR));
+           (vnode_vtype(vp) == VDIR), (options & FSOPT_ATTR_CMN_EXTENDED));
 
        namelen = strlen(namebuf);
        vsiz = namelen + 1;
@@ -3254,7 +3553,8 @@ readdirattr(vnode_t dvp, struct fd_vn_data *fvd, uio_t auio,
                error = getattrlist_internal(ctx, vp, &al,
                    CAST_USER_ADDR_T(kern_attr_buf), kern_attr_buf_siz,
                    options | FSOPT_REPORT_FULLSIZE, UIO_SYSSPACE, 
-                   CAST_DOWN_EXPLICIT(char *, name_buffer));
+                   CAST_DOWN_EXPLICIT(char *, name_buffer),
+                   NOCRED);
 
                nameidone(&nd);
 
@@ -3409,6 +3709,22 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
                goto out;
        }
 
+       if (uap->options & FSOPT_LIST_SNAPSHOT) {
+               vnode_t snapdvp;
+
+               if (!vnode_isvroot(dvp)) {
+                       error = EINVAL;
+                       goto out;
+               }
+
+               /* switch directory to snapshot directory */
+               error = vnode_get_snapdir(dvp, &snapdvp, ctx);
+               if (error)
+                       goto out;
+               vnode_put(dvp);
+               dvp = snapdvp;
+       }
+
        if (dvp->v_type != VDIR) {
                error = ENOTDIR;
                goto out;
@@ -3522,7 +3838,7 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
                        va.va_name = va_name;
 
                        (void)getattrlist_setupvattr_all(&al, &va, VNON, NULL,
-                           IS_64BIT_PROCESS(p));
+                           IS_64BIT_PROCESS(p), (uap->options & FSOPT_ATTR_CMN_EXTENDED));
 
                        error = VNOP_GETATTRLISTBULK(dvp, &al, &va, auio, NULL,
                            options, &eofflag, &count, ctx);
@@ -3636,6 +3952,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);
@@ -3822,6 +4151,15 @@ 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);
+               VATTR_SET_ACTIVE(&va, va_dataprotect_class);
+       }
 
        /* volume */
        if (al.volattr & ATTR_VOL_INFO) {
@@ -3910,12 +4248,19 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
                goto out;
        }
 
+#if CONFIG_MACF
+       mac_vnode_notify_setattrlist(ctx, vp, &al);
+       if (VATTR_IS_ACTIVE(&va, va_flags))
+               mac_vnode_notify_setflags(ctx, vp, va.va_flags);
+#endif
+
        /*
         * Write the Finder Info if we have any.
         */
        if (fndrinfo != NULL) {
                if (al.volattr & ATTR_VOL_INFO) {
                        if (vp->v_tag == VT_HFS) {
+#define HFS_SET_BOOT_INFO   (FCNTL_FS_SPECIFIC_BASE + 0x00005)
                                error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, ctx);
                                if (error != 0)
                                        goto out;
@@ -3996,6 +4341,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)
 {