]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_attrlist.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_attrlist.c
index 65f1560c7a6bddf2a512ba3f4e790645fe7df087..c0bc4c89e29f7bb5f255bc8e44bbbe19c88f300a 100644 (file)
@@ -1,8 +1,8 @@
 /*
- * Copyright (c) 1995-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 1995-2019 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- * 
+ *
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
  * unlawful or unlicensed copies of an Apple operating system, or to
  * circumvent, violate, or enable the circumvention or violation of, any
  * terms of an Apple operating system software license agreement.
- * 
+ *
  * Please obtain a copy of the License at
  * http://www.opensource.apple.com/apsl/ and read it before using this file.
- * 
+ *
  * The Original Code and all software distributed under the License are
  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
@@ -22,7 +22,7 @@
  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  * Please see the License for the specific language governing rights and
  * limitations under the License.
- * 
+ *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 /*
 #include <sys/file_internal.h>
 #include <sys/kauth.h>
 #include <sys/uio_internal.h>
-#include <sys/malloc.h>
+#include <kern/kalloc.h>
 #include <sys/attr.h>
 #include <sys/sysproto.h>
 #include <sys/xattr.h>
 #include <sys/fsevents.h>
-#include <kern/kalloc.h>
+#include <kern/zalloc.h>
 #include <miscfs/specfs/specdev.h>
+#include <security/audit/audit.h>
 
 #if CONFIG_MACF
 #include <security/mac_framework.h>
 #endif
 
-#define ATTR_TIME_SIZE -1
+#define ATTR_TIME_SIZE  -1
+
+static int readdirattr(vnode_t, struct fd_vn_data *, uio_t, struct attrlist *,
+    uint64_t, int *, int *, vfs_context_t ctx) __attribute__((noinline));
+
+static void
+vattr_get_alt_data(vnode_t, struct attrlist *, struct vnode_attr *, int, int,
+    int, vfs_context_t) __attribute__((noinline));
+
+static void get_error_attributes(vnode_t, struct attrlist *, uint64_t, user_addr_t,
+    size_t, int, caddr_t, vfs_context_t) __attribute__((noinline));
+
+static int getvolattrlist(vfs_context_t, vnode_t, struct attrlist *, user_addr_t,
+    size_t, uint64_t, enum uio_seg, int) __attribute__((noinline));
+
+static int get_direntry(vfs_context_t, vnode_t, struct fd_vn_data *, int *,
+    struct direntry **) __attribute__((noinline));
 
 /*
  * Structure describing the state of an in-progress attrlist operation.
  */
 struct _attrlist_buf {
-       char    *base;
-       char    *fixedcursor;
-       char    *varcursor;
-       ssize_t allocated;
+       char    *base;
+       char    *fixedcursor;
+       char    *varcursor;
+       ssize_t allocated;
        ssize_t needed;
-       attribute_set_t actual;
+       attribute_set_t actual;
        attribute_set_t valid;
 };
 
@@ -79,25 +96,25 @@ struct _attrlist_buf {
 static void
 attrlist_pack_fixed(struct _attrlist_buf *ab, void *source, ssize_t count)
 {
-       /* 
+       /*
         * Use ssize_t for pointer math purposes,
         * since a ssize_t is a signed long
         */
-       ssize_t fit;
+       ssize_t fit;
 
        /*
         * Compute the amount of remaining space in the attrlist buffer
         * based on how much we've used for fixed width fields vs. the
-        * start of the attributes.  
-        * 
-        * If we've still got room, then 'fit' will contain the amount of 
-        * remaining space.  
-        * 
-        * Note that this math is safe because, in the event that the 
+        * start of the attributes.
+        *
+        * If we've still got room, then 'fit' will contain the amount of
+        * remaining space.
+        *
+        * Note that this math is safe because, in the event that the
         * fixed-width cursor has moved beyond the end of the buffer,
-        * then, the second input into lmin() below will be negative, and 
-        * we will fail the (fit > 0) check below. 
-        */ 
+        * then, the second input into lmin() below will be negative, and
+        * we will fail the (fit > 0) check below.
+        */
        fit = lmin(count, ab->allocated - (ab->fixedcursor - ab->base));
        if (fit > 0) {
                /* Copy in as much as we can */
@@ -112,34 +129,34 @@ attrlist_pack_fixed(struct _attrlist_buf *ab, void *source, ssize_t count)
  * Attempt to pack one (or two) variable width attributes into the attrlist
  * buffer.  If we are trying to pack two variable width attributes, they are treated
  * as a single variable-width attribute from the POV of the system call caller.
- * 
- * Recall that a variable-width attribute has two components: the fixed-width 
+ *
+ * Recall that a variable-width attribute has two components: the fixed-width
  * attribute that tells the caller where to look, and the actual variable width data.
  */
 static void
-attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t count, 
-               const void *ext, ssize_t extcount) 
+attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t count,
+    const void *ext, ssize_t extcount)
 {
        /* Use ssize_t's for pointer math ease */
        struct attrreference ar;
        ssize_t fit;
 
        /*
-        * Pack the fixed-width component to the variable object. 
+        * Pack the fixed-width component to the variable object.
         * Note that we may be able to pack the fixed width attref, but not
         * the variable (if there's no room).
         */
-       ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
-       ar.attr_length = count + extcount;
+       ar.attr_dataoffset = (int32_t)(ab->varcursor - ab->fixedcursor);
+       ar.attr_length = (u_int32_t)(count + extcount);
        attrlist_pack_fixed(ab, &ar, sizeof(ar));
 
-       /* 
+       /*
         * Use an lmin() to do a signed comparison. We use a signed comparison
         * to detect the 'out of memory' conditions as described above in the
         * fixed width check above.
         *
         * Then pack the first variable attribute as space allows.  Note that we advance
-        * the variable cursor only if we we had some available space. 
+        * the variable cursor only if we we had some available space.
         */
        fit = lmin(count, ab->allocated - (ab->varcursor - ab->base));
        if (fit > 0) {
@@ -162,7 +179,7 @@ attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t co
        ab->varcursor = (char *)roundup((uintptr_t)ab->varcursor, 4);
 }
 
-/* 
+/*
  * Packing a single variable-width attribute is the same as calling the two, but with
  * an invalid 2nd attribute.
  */
@@ -187,7 +204,7 @@ attrlist_pack_variable(struct _attrlist_buf *ab, const void *source, ssize_t cou
  * allocated buffer space.
  */
 static void
-attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count)
+attrlist_pack_string(struct _attrlist_buf *ab, const char *source, size_t count)
 {
        struct attrreference ar;
        ssize_t fit, space;
@@ -203,10 +220,10 @@ attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count
        }
 
        /*
-        * Construct the fixed-width attribute that refers to this string. 
+        * Construct the fixed-width attribute that refers to this string.
         */
-       ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
-       ar.attr_length = count + 1;
+       ar.attr_dataoffset = (int32_t)(ab->varcursor - ab->fixedcursor);
+       ar.attr_length = (u_int32_t)count + 1;
        attrlist_pack_fixed(ab, &ar, sizeof(ar));
 
        /*
@@ -223,10 +240,10 @@ attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count
        space = ab->allocated - (ab->varcursor - ab->base);
        fit = lmin(count, space);
        if (space > 0) {
-               int bytes_to_zero;
+               long bytes_to_zero;
 
-               /* 
-                * If there is space remaining, copy data in, and 
+               /*
+                * If there is space remaining, copy data in, and
                 * accommodate the trailing NUL terminator.
                 *
                 * NOTE: if "space" is too small to hold the string and its NUL
@@ -245,51 +262,51 @@ attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count
                         * Zero out any additional bytes we might have as a
                         * result of rounding up.
                         */
-                       bytes_to_zero = min((roundup(fit, 4) - fit),
+                       bytes_to_zero = lmin((roundup(fit, 4) - fit),
                            space - fit);
-                       if (bytes_to_zero)
+                       if (bytes_to_zero) {
                                bzero(&(ab->varcursor[fit]), bytes_to_zero);
+                       }
                }
        }
-       /* 
+       /*
         * always move in increments of 4 (including the trailing NUL)
         */
-       ab->varcursor += roundup((count+1), 4);
-
+       ab->varcursor += roundup((count + 1), 4);
 }
 
 #define ATTR_PACK4(AB, V)                                                 \
        do {                                                              \
-               if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 4) {   \
-                       *(uint32_t *)AB.fixedcursor = V;                  \
-                       AB.fixedcursor += 4;                              \
-               }                                                         \
+               if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 4) {   \
+                       *(uint32_t *)AB.fixedcursor = V;                  \
+                       AB.fixedcursor += 4;                              \
+               }                                                         \
        } while (0)
 
 #define ATTR_PACK8(AB, V)                                                 \
        do {                                                              \
-               if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 8) {   \
-                       *(uint64_t *)AB.fixedcursor = *(uint64_t *)&V;    \
-                       AB.fixedcursor += 8;                              \
-               }                                                         \
+               if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 8) {   \
+                       memcpy(AB.fixedcursor, &V, 8);                    \
+                       AB.fixedcursor += 8;                              \
+               }                                                         \
        } while (0)
 
-#define ATTR_PACK(b, v)        attrlist_pack_fixed(b, &v, sizeof(v))
-#define ATTR_PACK_CAST(b, t, v)                                                \
-       do {                                                            \
-               t _f = (t)v;                                            \
-               ATTR_PACK(b, _f);                                       \
+#define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
+#define ATTR_PACK_CAST(b, t, v)                                         \
+       do {                                                            \
+               t _f = (t)v;                                            \
+               ATTR_PACK(b, _f);                                       \
        } while (0)
 
-#define ATTR_PACK_TIME(b, v, is64)                                                     \
-       do {                                                                            \
-               if (is64) {                                                             \
-                       struct user64_timespec us = {v.tv_sec, v.tv_nsec};              \
-                       ATTR_PACK(&b, us);                                              \
-               } else {                                                                \
-                       struct user32_timespec us = {v.tv_sec, v.tv_nsec};              \
-                       ATTR_PACK(&b, us);                                              \
-               }                                                                       \
+#define ATTR_PACK_TIME(b, v, is64)                                                      \
+       do {                                                                            \
+               if (is64) {                                                             \
+                       struct user64_timespec us = {.tv_sec = v.tv_sec, .tv_nsec = v.tv_nsec};         \
+                       ATTR_PACK(&b, us);                                              \
+               } else {                                                                \
+                       struct user32_timespec us = {.tv_sec = (user32_time_t)v.tv_sec, .tv_nsec = (user32_long_t)v.tv_nsec};         \
+                       ATTR_PACK(&b, us);                                              \
+               }                                                                       \
        } while(0)
 
 
@@ -297,75 +314,77 @@ attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count
  * Table-driven setup for all valid common/volume attributes.
  */
 struct getvolattrlist_attrtab {
-       attrgroup_t     attr;
-       uint64_t        bits;
-#define VFSATTR_BIT(b) (VFSATTR_ ## b)
-       ssize_t         size;
+       attrgroup_t     attr;
+       uint64_t        bits;
+#define VFSATTR_BIT(b)  (VFSATTR_ ## b)
+       ssize_t         size;
 };
 static struct getvolattrlist_attrtab getvolattrlist_common_tab[] = {
-       {ATTR_CMN_NAME,         0,                              sizeof(struct attrreference)},
-       {ATTR_CMN_DEVID,        0,                              sizeof(dev_t)},
-       {ATTR_CMN_FSID,         0,                              sizeof(fsid_t)},
-       {ATTR_CMN_OBJTYPE,      0,                              sizeof(fsobj_type_t)},
-       {ATTR_CMN_OBJTAG,       0,                              sizeof(fsobj_tag_t)},
-       {ATTR_CMN_OBJID,        0,                              sizeof(fsobj_id_t)},
-       {ATTR_CMN_OBJPERMANENTID, 0,                            sizeof(fsobj_id_t)},
-       {ATTR_CMN_PAROBJID,     0,                              sizeof(fsobj_id_t)},
-       {ATTR_CMN_SCRIPT,       0,                              sizeof(text_encoding_t)},
-       {ATTR_CMN_CRTIME,       VFSATTR_BIT(f_create_time),     ATTR_TIME_SIZE},
-       {ATTR_CMN_MODTIME,      VFSATTR_BIT(f_modify_time),     ATTR_TIME_SIZE},
-       {ATTR_CMN_CHGTIME,      VFSATTR_BIT(f_modify_time),     ATTR_TIME_SIZE},
-       {ATTR_CMN_ACCTIME,      VFSATTR_BIT(f_access_time),     ATTR_TIME_SIZE},
-       {ATTR_CMN_BKUPTIME,     VFSATTR_BIT(f_backup_time),     ATTR_TIME_SIZE},
-       {ATTR_CMN_FNDRINFO,     0,                              32},
-       {ATTR_CMN_OWNERID,      0,                              sizeof(uid_t)},
-       {ATTR_CMN_GRPID,        0,                              sizeof(gid_t)},
-       {ATTR_CMN_ACCESSMASK,   0,                              sizeof(uint32_t)},
-       {ATTR_CMN_FLAGS,        0,                              sizeof(uint32_t)},
-       {ATTR_CMN_USERACCESS,   0,                              sizeof(uint32_t)},
-       {ATTR_CMN_EXTENDED_SECURITY, 0,                         sizeof(struct attrreference)},
-       {ATTR_CMN_UUID,         0,                              sizeof(guid_t)},
-       {ATTR_CMN_GRPUUID,      0,                              sizeof(guid_t)},
-       {ATTR_CMN_FILEID,       0,                              sizeof(uint64_t)},
-       {ATTR_CMN_PARENTID,     0,                              sizeof(uint64_t)},
-       {ATTR_CMN_RETURNED_ATTRS, 0,                            sizeof(attribute_set_t)},
-       {ATTR_CMN_ERROR,        0,                              sizeof(uint32_t)},
-       {0, 0, 0}
+       {.attr = ATTR_CMN_NAME, .bits = 0, .size = sizeof(struct attrreference)},
+       {.attr = ATTR_CMN_DEVID, .bits = 0, .size = sizeof(dev_t)},
+       {.attr = ATTR_CMN_FSID, .bits = 0, .size = sizeof(fsid_t)},
+       {.attr = ATTR_CMN_OBJTYPE, .bits = 0, .size = sizeof(fsobj_type_t)},
+       {.attr = ATTR_CMN_OBJTAG, .bits = 0, .size = sizeof(fsobj_tag_t)},
+       {.attr = ATTR_CMN_OBJID, .bits = 0, .size = sizeof(fsobj_id_t)},
+       {.attr = ATTR_CMN_OBJPERMANENTID, .bits = 0, .size = sizeof(fsobj_id_t)},
+       {.attr = ATTR_CMN_PAROBJID, .bits = 0, .size = sizeof(fsobj_id_t)},
+       {.attr = ATTR_CMN_SCRIPT, .bits = 0, .size = sizeof(text_encoding_t)},
+       {.attr = ATTR_CMN_CRTIME, .bits = VFSATTR_BIT(f_create_time), .size = ATTR_TIME_SIZE},
+       {.attr = ATTR_CMN_MODTIME, .bits = VFSATTR_BIT(f_modify_time), .size = ATTR_TIME_SIZE},
+       {.attr = ATTR_CMN_CHGTIME, .bits = VFSATTR_BIT(f_modify_time), .size = ATTR_TIME_SIZE},
+       {.attr = ATTR_CMN_ACCTIME, .bits = VFSATTR_BIT(f_access_time), .size = ATTR_TIME_SIZE},
+       {.attr = ATTR_CMN_BKUPTIME, .bits = VFSATTR_BIT(f_backup_time), .size = ATTR_TIME_SIZE},
+       {.attr = ATTR_CMN_FNDRINFO, .bits = 0, .size = 32},
+       {.attr = ATTR_CMN_OWNERID, .bits = 0, .size = sizeof(uid_t)},
+       {.attr = ATTR_CMN_GRPID, .bits = 0, .size = sizeof(gid_t)},
+       {.attr = ATTR_CMN_ACCESSMASK, .bits = 0, .size = sizeof(uint32_t)},
+       {.attr = ATTR_CMN_FLAGS, .bits = 0, .size = sizeof(uint32_t)},
+       {.attr = ATTR_CMN_USERACCESS, .bits = 0, .size = sizeof(uint32_t)},
+       {.attr = ATTR_CMN_EXTENDED_SECURITY, .bits = 0, .size = sizeof(struct attrreference)},
+       {.attr = ATTR_CMN_UUID, .bits = 0, .size = sizeof(guid_t)},
+       {.attr = ATTR_CMN_GRPUUID, .bits = 0, .size = sizeof(guid_t)},
+       {.attr = ATTR_CMN_FILEID, .bits = 0, .size = sizeof(uint64_t)},
+       {.attr = ATTR_CMN_PARENTID, .bits = 0, .size = sizeof(uint64_t)},
+       {.attr = ATTR_CMN_RETURNED_ATTRS, .bits = 0, .size = sizeof(attribute_set_t)},
+       {.attr = ATTR_CMN_ERROR, .bits = 0, .size = sizeof(uint32_t)},
+       {.attr = 0, .bits = 0, .size = 0}
 };
 #define ATTR_CMN_VOL_INVALID \
        (ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID | \
         ATTR_CMN_FILEID | ATTR_CMN_PARENTID)
 
 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_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)},
-       {ATTR_VOL_ALLOCATIONCLUMP,      VFSATTR_BIT(f_bsize),                           sizeof(off_t)},
-       {ATTR_VOL_IOBLOCKSIZE,          VFSATTR_BIT(f_iosize),                          sizeof(uint32_t)},
-       {ATTR_VOL_OBJCOUNT,             VFSATTR_BIT(f_objcount),                        sizeof(uint32_t)},
-       {ATTR_VOL_FILECOUNT,            VFSATTR_BIT(f_filecount),                       sizeof(uint32_t)},
-       {ATTR_VOL_DIRCOUNT,             VFSATTR_BIT(f_dircount),                        sizeof(uint32_t)},
-       {ATTR_VOL_MAXOBJCOUNT,          VFSATTR_BIT(f_maxobjcount),                     sizeof(uint32_t)},
-       {ATTR_VOL_MOUNTPOINT,           0,                                              sizeof(struct attrreference)},
-       {ATTR_VOL_NAME,                 VFSATTR_BIT(f_vol_name),                        sizeof(struct attrreference)},
-       {ATTR_VOL_MOUNTFLAGS,           0,                                              sizeof(uint32_t)},
-       {ATTR_VOL_MOUNTEDDEVICE,        0,                                              sizeof(struct attrreference)},
-       {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_ATTRIBUTES,           VFSATTR_BIT(f_attributes),                      sizeof(vol_attributes_attr_t)},
-       {ATTR_VOL_INFO, 0, 0},
-       {0, 0, 0}
+       {.attr = ATTR_VOL_FSTYPE, .bits = 0, .size = sizeof(uint32_t)},
+       {.attr = ATTR_VOL_SIGNATURE, .bits = VFSATTR_BIT(f_signature), .size = sizeof(uint32_t)},
+       {.attr = ATTR_VOL_SIZE, .bits = VFSATTR_BIT(f_blocks)  |  VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+       {.attr = ATTR_VOL_SPACEFREE, .bits = VFSATTR_BIT(f_bfree) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+       {.attr = ATTR_VOL_SPACEAVAIL, .bits = VFSATTR_BIT(f_bavail) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+       {.attr = ATTR_VOL_MINALLOCATION, .bits = VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+       {.attr = ATTR_VOL_ALLOCATIONCLUMP, .bits = VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+       {.attr = ATTR_VOL_IOBLOCKSIZE, .bits = VFSATTR_BIT(f_iosize), .size = sizeof(uint32_t)},
+       {.attr = ATTR_VOL_OBJCOUNT, .bits = VFSATTR_BIT(f_objcount), .size = sizeof(uint32_t)},
+       {.attr = ATTR_VOL_FILECOUNT, .bits = VFSATTR_BIT(f_filecount), .size = sizeof(uint32_t)},
+       {.attr = ATTR_VOL_DIRCOUNT, .bits = VFSATTR_BIT(f_dircount), .size = sizeof(uint32_t)},
+       {.attr = ATTR_VOL_MAXOBJCOUNT, .bits = VFSATTR_BIT(f_maxobjcount), .size = sizeof(uint32_t)},
+       {.attr = ATTR_VOL_MOUNTPOINT, .bits = 0, .size = sizeof(struct attrreference)},
+       {.attr = ATTR_VOL_NAME, .bits = VFSATTR_BIT(f_vol_name), .size = sizeof(struct attrreference)},
+       {.attr = ATTR_VOL_MOUNTFLAGS, .bits = 0, .size = sizeof(uint32_t)},
+       {.attr = ATTR_VOL_MOUNTEDDEVICE, .bits = 0, .size = sizeof(struct attrreference)},
+       {.attr = ATTR_VOL_ENCODINGSUSED, .bits = 0, .size = sizeof(uint64_t)},
+       {.attr = ATTR_VOL_CAPABILITIES, .bits = VFSATTR_BIT(f_capabilities), .size = sizeof(vol_capabilities_attr_t)},
+       {.attr = ATTR_VOL_UUID, .bits = VFSATTR_BIT(f_uuid), .size = sizeof(uuid_t)},
+       {.attr = ATTR_VOL_QUOTA_SIZE, .bits = VFSATTR_BIT(f_quota) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+       {.attr = ATTR_VOL_RESERVED_SIZE, .bits = VFSATTR_BIT(f_reserved) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+       {.attr = ATTR_VOL_ATTRIBUTES, .bits = VFSATTR_BIT(f_attributes), .size = sizeof(vol_attributes_attr_t)},
+       {.attr = ATTR_VOL_INFO, .bits = 0, .size = 0},
+       {.attr = 0, .bits = 0, .size = 0}
 };
 
 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;
+       attrgroup_t     recognised;
 
        recognised = 0;
        do {
@@ -383,12 +402,13 @@ 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)
-               return(EINVAL);
-       return(0);
+       if (attrs & ~recognised) {
+               return EINVAL;
+       }
+       return 0;
 }
 
 /*
@@ -398,28 +418,33 @@ getvolattrlist_parsetab(struct getvolattrlist_attrtab *tab, attrgroup_t attrs, s
 static int
 getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t *sizep, int is_64bit)
 {
-       int     error;
+       int     error;
+       if (!alp) {
+               return EINVAL;
+       }
 
        /*
         * Parse the above tables.
         */
-       *sizep = sizeof(uint32_t);      /* length count */
+       *sizep = sizeof(uint32_t);      /* length count */
        if (alp->commonattr) {
                if ((alp->commonattr & ATTR_CMN_VOL_INVALID) &&
                    (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) == 0) {
-                       return (EINVAL);
+                       return EINVAL;
                }
                if ((error = getvolattrlist_parsetab(getvolattrlist_common_tab,
-                                                   alp->commonattr, vsp, sizep,
-                                                   is_64bit)) != 0) {
-                       return(error);
+                   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)
-               return(error);
+           (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);
+       return 0;
 }
 
 /*
@@ -458,71 +483,89 @@ getvolattrlist_fixupattrs(attribute_set_t *asp, struct vfs_attr *vsp)
  * Table-driven setup for all valid common/dir/file/fork attributes against files.
  */
 struct getattrlist_attrtab {
-       attrgroup_t     attr;
-       uint64_t        bits;
-#define VATTR_BIT(b)   (VNODE_ATTR_ ## b)
-       ssize_t         size;
-       kauth_action_t  action;
+       attrgroup_t     attr;
+       uint64_t        bits;
+#define VATTR_BIT(b)    (VNODE_ATTR_ ## b)
+       ssize_t         size;
+       kauth_action_t  action;
 };
 
-/* 
- * A zero after the ATTR_ bit indicates that we don't expect the underlying FS to report back with this 
+/*
+ * A zero after the ATTR_ bit indicates that we don't expect the underlying FS to report back with this
  * information, and we will synthesize it at the VFS level.
  */
 static struct getattrlist_attrtab getattrlist_common_tab[] = {
-       {ATTR_CMN_NAME,         VATTR_BIT(va_name),             sizeof(struct attrreference),   KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_DEVID,        0,                              sizeof(dev_t),                  KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_FSID,         0,                              sizeof(fsid_t),                 KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_OBJTYPE,      0,                              sizeof(fsobj_type_t),           KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_OBJTAG,       0,                              sizeof(fsobj_tag_t),            KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_OBJID,        VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_OBJPERMANENTID, VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_PAROBJID,     VATTR_BIT(va_parentid),         sizeof(fsobj_id_t),             KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_SCRIPT,       VATTR_BIT(va_encoding),         sizeof(text_encoding_t),        KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_CRTIME,       VATTR_BIT(va_create_time),      ATTR_TIME_SIZE,                 KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_MODTIME,      VATTR_BIT(va_modify_time),      ATTR_TIME_SIZE,                 KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_CHGTIME,      VATTR_BIT(va_change_time),      ATTR_TIME_SIZE,                 KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_ACCTIME,      VATTR_BIT(va_access_time),      ATTR_TIME_SIZE,                 KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_BKUPTIME,     VATTR_BIT(va_backup_time),      ATTR_TIME_SIZE,                 KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_FNDRINFO,     0,                              32,                             KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_OWNERID,      VATTR_BIT(va_uid),              sizeof(uid_t),                  KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_GRPID,        VATTR_BIT(va_gid),              sizeof(gid_t),                  KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_ACCESSMASK,   VATTR_BIT(va_mode),             sizeof(uint32_t),               KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_FLAGS,        VATTR_BIT(va_flags),            sizeof(uint32_t),               KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_GEN_COUNT,    VATTR_BIT(va_write_gencount),   sizeof(uint32_t),               KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_DOCUMENT_ID,  VATTR_BIT(va_document_id),      sizeof(uint32_t),               KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_USERACCESS,   0,                              sizeof(uint32_t),               KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_EXTENDED_SECURITY, VATTR_BIT(va_acl),         sizeof(struct attrreference),   KAUTH_VNODE_READ_SECURITY},
-       {ATTR_CMN_UUID,         VATTR_BIT(va_uuuid),            sizeof(guid_t),                 KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_GRPUUID,      VATTR_BIT(va_guuid),            sizeof(guid_t),                 KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_FILEID,       VATTR_BIT(va_fileid),           sizeof(uint64_t),               KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_PARENTID,     VATTR_BIT(va_parentid),         sizeof(uint64_t),               KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_FULLPATH,     0,                              sizeof(struct attrreference),   KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_ADDEDTIME,    VATTR_BIT(va_addedtime),        ATTR_TIME_SIZE,                 KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_RETURNED_ATTRS, 0,                            sizeof(attribute_set_t),        0},
-       {ATTR_CMN_ERROR,        0,                              sizeof(uint32_t),               0},
-       {ATTR_CMN_DATA_PROTECT_FLAGS, VATTR_BIT(va_dataprotect_class), sizeof(uint32_t),        KAUTH_VNODE_READ_ATTRIBUTES},
-       {0, 0, 0, 0}
+       {.attr = ATTR_CMN_NAME, .bits = VATTR_BIT(va_name), .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_DEVID, .bits = VATTR_BIT(va_fsid), .size = sizeof(dev_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_FSID, .bits = VATTR_BIT(va_fsid64), .size = sizeof(fsid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_OBJTYPE, .bits = 0, .size = sizeof(fsobj_type_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_OBJTAG, .bits = 0, .size = sizeof(fsobj_tag_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_OBJID, .bits = VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), .size = sizeof(fsobj_id_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_OBJPERMANENTID, .bits = VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), .size = sizeof(fsobj_id_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_PAROBJID, .bits = VATTR_BIT(va_parentid), .size = sizeof(fsobj_id_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_SCRIPT, .bits = VATTR_BIT(va_encoding), .size = sizeof(text_encoding_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_CRTIME, .bits = VATTR_BIT(va_create_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_MODTIME, .bits = VATTR_BIT(va_modify_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_CHGTIME, .bits = VATTR_BIT(va_change_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_ACCTIME, .bits = VATTR_BIT(va_access_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_BKUPTIME, .bits = VATTR_BIT(va_backup_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_FNDRINFO, .bits = 0, .size = 32, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_OWNERID, .bits = VATTR_BIT(va_uid), .size = sizeof(uid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_GRPID, .bits = VATTR_BIT(va_gid), .size = sizeof(gid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_ACCESSMASK, .bits = VATTR_BIT(va_mode), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_FLAGS, .bits = VATTR_BIT(va_flags), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_GEN_COUNT, .bits = VATTR_BIT(va_write_gencount), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_DOCUMENT_ID, .bits = VATTR_BIT(va_document_id), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_USERACCESS, .bits = 0, .size = sizeof(uint32_t), .action = 0},
+       {.attr = ATTR_CMN_EXTENDED_SECURITY, .bits = VATTR_BIT(va_acl), .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_SECURITY},
+       {.attr = ATTR_CMN_UUID, .bits = VATTR_BIT(va_uuuid), .size = sizeof(guid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_GRPUUID, .bits = VATTR_BIT(va_guuid), .size = sizeof(guid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_FILEID, .bits = VATTR_BIT(va_fileid), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_PARENTID, .bits = VATTR_BIT(va_parentid), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_FULLPATH, .bits = 0, .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_ADDEDTIME, .bits = VATTR_BIT(va_addedtime), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_RETURNED_ATTRS, .bits = 0, .size = sizeof(attribute_set_t), .action = 0},
+       {.attr = ATTR_CMN_ERROR, .bits = 0, .size = sizeof(uint32_t), .action = 0},
+       {.attr = ATTR_CMN_DATA_PROTECT_FLAGS, .bits = VATTR_BIT(va_dataprotect_class), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = 0, .bits = 0, .size = 0, .action = 0}
 };
 
 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},
-       {0, 0, 0, 0}
+       {.attr = ATTR_DIR_LINKCOUNT, .bits = VATTR_BIT(va_dirlinkcount), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_DIR_ENTRYCOUNT, .bits = VATTR_BIT(va_nchildren), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_DIR_MOUNTSTATUS, .bits = 0, .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_DIR_ALLOCSIZE, .bits = VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_DIR_IOBLOCKSIZE, .bits = VATTR_BIT(va_iosize), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_DIR_DATALENGTH, .bits = VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = 0, .bits = 0, .size = 0, .action = 0}
 };
 static struct getattrlist_attrtab getattrlist_file_tab[] = {
-       {ATTR_FILE_LINKCOUNT,   VATTR_BIT(va_nlink),            sizeof(uint32_t),               KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_FILE_TOTALSIZE,   VATTR_BIT(va_total_size),       sizeof(off_t),                  KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_FILE_ALLOCSIZE,   VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_FILE_IOBLOCKSIZE, VATTR_BIT(va_iosize),           sizeof(uint32_t),               KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_FILE_DEVTYPE,     VATTR_BIT(va_rdev),             sizeof(dev_t),                  KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_FILE_DATALENGTH,  VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_FILE_DATAALLOCSIZE, VATTR_BIT(va_total_alloc)| VATTR_BIT(va_data_alloc), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
-       {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}
-};     
+       {.attr = ATTR_FILE_LINKCOUNT, .bits = VATTR_BIT(va_nlink), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_FILE_TOTALSIZE, .bits = VATTR_BIT(va_total_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_FILE_ALLOCSIZE, .bits = VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_FILE_IOBLOCKSIZE, .bits = VATTR_BIT(va_iosize), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_FILE_CLUMPSIZE, .bits = 0, .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_FILE_DEVTYPE, .bits = VATTR_BIT(va_rdev), .size = sizeof(dev_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_FILE_DATALENGTH, .bits = VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_FILE_DATAALLOCSIZE, .bits = VATTR_BIT(va_total_alloc) | VATTR_BIT(va_data_alloc), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_FILE_RSRCLENGTH, .bits = 0, .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_FILE_RSRCALLOCSIZE, .bits = 0, .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = 0, .bits = 0, .size = 0, .action = 0}
+};
+
+//for forkattr bits repurposed as new common attributes
+static struct getattrlist_attrtab getattrlist_common_extended_tab[] = {
+       {.attr = ATTR_CMNEXT_RELPATH, .bits = 0, .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMNEXT_PRIVATESIZE, .bits = VATTR_BIT(va_private_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMNEXT_LINKID, .bits = VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMNEXT_NOFIRMLINKPATH, .bits = 0, .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMNEXT_REALDEVID, .bits = VATTR_BIT(va_devid), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMNEXT_REALFSID, .bits = VATTR_BIT(va_fsid64), .size = sizeof(fsid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMNEXT_CLONEID, .bits = VATTR_BIT(va_clone_id), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMNEXT_EXT_FLAGS, .bits = VATTR_BIT(va_extflags), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMNEXT_RECURSIVE_GENCOUNT, .bits = VATTR_BIT(va_recursive_gencount), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = 0, .bits = 0, .size = 0, .action = 0}
+};
 
 /*
  * This table is for attributes which are only set from the getattrlistbulk(2)
@@ -534,19 +577,25 @@ static struct getattrlist_attrtab getattrlist_file_tab[] = {
  * accounted from the common, file and directory tables.
  */
 static struct getattrlist_attrtab getattrlistbulk_common_tab[] = {
-       {ATTR_CMN_DEVID,        VATTR_BIT(va_devid),            0,                      KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_FSID,         VATTR_BIT(va_fsid64),           0,                      KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_OBJTYPE,      VATTR_BIT(va_objtype),          0,                      KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_OBJTAG,       VATTR_BIT(va_objtag),           0,                      KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_USERACCESS,   VATTR_BIT(va_user_access),      0,                      KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_CMN_FNDRINFO,     VATTR_BIT(va_finderinfo),       0,                      KAUTH_VNODE_READ_ATTRIBUTES},
-       {0, 0, 0, 0}
+       {.attr = ATTR_CMN_DEVID, .bits = VATTR_BIT(va_devid), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_FSID, .bits = VATTR_BIT(va_fsid64), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_OBJTYPE, .bits = VATTR_BIT(va_objtype), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_OBJTAG, .bits = VATTR_BIT(va_objtag), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_CMN_USERACCESS, .bits = VATTR_BIT(va_user_access), .size = 0, .action = 0},
+       {.attr = ATTR_CMN_FNDRINFO, .bits = VATTR_BIT(va_finderinfo), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = 0, .bits = 0, .size = 0, .action = 0}
 };
 
 static struct getattrlist_attrtab getattrlistbulk_file_tab[] = {
-       {ATTR_FILE_RSRCLENGTH,  VATTR_BIT(va_rsrc_length),      0,                      KAUTH_VNODE_READ_ATTRIBUTES},
-       {ATTR_FILE_RSRCALLOCSIZE, VATTR_BIT(va_rsrc_alloc),     0,                      KAUTH_VNODE_READ_ATTRIBUTES},
-       {0, 0, 0, 0}
+       {.attr = ATTR_FILE_RSRCLENGTH, .bits = VATTR_BIT(va_rsrc_length), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = ATTR_FILE_RSRCALLOCSIZE, .bits = VATTR_BIT(va_rsrc_alloc), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+       {.attr = 0, .bits = 0, .size = 0, .action = 0}
+};
+
+static struct getattrlist_attrtab getattrlistbulk_common_extended_tab[] = {
+       /* getattrlist_parsetab() expects > 1 entries */
+       {.attr = 0, .bits = 0, .size = 0, .action = 0},
+       {.attr = 0, .bits = 0, .size = 0, .action = 0}
 };
 
 /*
@@ -554,76 +603,85 @@ static struct getattrlist_attrtab getattrlistbulk_file_tab[] = {
  *
  * 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_SPACEAVAIL | ATTR_VOL_MINALLOCATION |  \
-                                ATTR_VOL_ALLOCATIONCLUMP |  ATTR_VOL_IOBLOCKSIZE |  \
-                                ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS |  \
-                                ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES |  \
-                                ATTR_VOL_ATTRIBUTES | ATTR_VOL_ENCODINGSUSED)
-
-#define VFS_DFLT_ATTR_CMN      (ATTR_CMN_NAME | ATTR_CMN_DEVID |  \
-                                ATTR_CMN_FSID | ATTR_CMN_OBJTYPE |  \
-                                ATTR_CMN_OBJTAG | ATTR_CMN_OBJID |  \
-                                ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT |  \
-                                ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME |  \
-                                ATTR_CMN_FNDRINFO |  \
-                                ATTR_CMN_OWNERID  | ATTR_CMN_GRPID |  \
-                                ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS |  \
-                                ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
-                                ATTR_CMN_PARENTID | ATTR_CMN_RETURNED_ATTRS | \
-                                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_DIR      (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
-
-#define VFS_DFLT_ATTR_FILE     (ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE |  \
-                                ATTR_FILE_ALLOCSIZE  | ATTR_FILE_IOBLOCKSIZE |  \
-                                ATTR_FILE_DEVTYPE | ATTR_FILE_DATALENGTH |  \
-                                ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_RSRCLENGTH |  \
-                                ATTR_FILE_RSRCALLOCSIZE)
+#define VFS_DFLT_ATTR_VOL       (ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE |  \
+                                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 |  \
+                                ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES |  \
+                                ATTR_VOL_ATTRIBUTES | ATTR_VOL_ENCODINGSUSED)
+
+#define VFS_DFLT_ATTR_CMN       (ATTR_CMN_NAME | ATTR_CMN_DEVID |  \
+                                ATTR_CMN_FSID | ATTR_CMN_OBJTYPE |  \
+                                ATTR_CMN_OBJTAG | ATTR_CMN_OBJID |  \
+                                ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT |  \
+                                ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME |  \
+                                ATTR_CMN_FNDRINFO |  \
+                                ATTR_CMN_OWNERID  | ATTR_CMN_GRPID |  \
+                                ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS |  \
+                                ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
+                                ATTR_CMN_PARENTID | ATTR_CMN_RETURNED_ATTRS | \
+                                ATTR_CMN_DOCUMENT_ID | ATTR_CMN_GEN_COUNT | \
+                                ATTR_CMN_DATA_PROTECT_FLAGS)
+
+#define VFS_DFLT_ATTR_CMN_EXT   (ATTR_CMNEXT_PRIVATESIZE | ATTR_CMNEXT_LINKID |  \
+                                ATTR_CMNEXT_NOFIRMLINKPATH | ATTR_CMNEXT_REALDEVID |  \
+                                ATTR_CMNEXT_REALFSID | ATTR_CMNEXT_CLONEID | \
+                                ATTR_CMNEXT_EXT_FLAGS)
+
+#define VFS_DFLT_ATTR_DIR       (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
+
+#define VFS_DFLT_ATTR_FILE      (ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE |  \
+                                ATTR_FILE_ALLOCSIZE  | ATTR_FILE_IOBLOCKSIZE |  \
+                                ATTR_FILE_DEVTYPE | ATTR_FILE_DATALENGTH |  \
+                                ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_RSRCLENGTH |  \
+                                ATTR_FILE_RSRCALLOCSIZE)
 
 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;
-
+       attrgroup_t     recognised;
        recognised = 0;
+       if (!tab) {
+               return EINVAL;
+       }
+
        do {
                /* is this attribute set? */
                if (tab->attr & attrs) {
                        recognised |= tab->attr;
-                       if (vap)
+                       if (vap) {
                                vap->va_active |= tab->bits;
+                       }
                        if (sizep) {
                                if (tab->size == ATTR_TIME_SIZE) {
                                        if (is_64bit) {
                                                *sizep += sizeof(
-                                                   struct user64_timespec);
+                                                       struct user64_timespec);
                                        } else {
                                                *sizep += sizeof(
-                                                   struct user32_timespec);
+                                                       struct user32_timespec);
                                        }
                                } else {
                                        *sizep += tab->size;
                                }
                        }
-                       if (actionp)
+                       if (actionp) {
                                *actionp |= tab->action;
-                       if (attrs == recognised)
+                       }
+                       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)
-               return(EINVAL);
-       return(0);
+       if (attrs & ~recognised) {
+               return EINVAL;
+       }
+       return 0;
 }
 
 /*
@@ -631,26 +689,33 @@ 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;
+       int     error;
 
        /*
         * Parse the above tables.
         */
-       *sizep = sizeof(uint32_t);      /* length count */
+       *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)
-               return(error);
+           (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)
-               return(error);
+           (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)
-               return(error);
+           (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);
+       return 0;
 }
 
 /*
@@ -659,9 +724,9 @@ 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;
+       int     error = 0;
 
        /*
         * Parse the above tables.
@@ -671,45 +736,62 @@ 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);
-                       /*
-                        * turn off va_fsid since we will be using only
-                        * va_fsid64 for ATTR_CMN_FSID.
-                        */
-                       VATTR_CLEAR_ACTIVE(vap, va_fsid);
+                           alp->commonattr, vap, fixedsize, NULL, is_64bit,
+                           sizeof(getattrlistbulk_common_tab) / sizeof(getattrlistbulk_common_tab[0]));
                }
        }
 
        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]));
                }
        }
 
-       return (error);
+       /* 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]));
+               }
+       }
+
+       return error;
 }
 
 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)
 {
-       return (getattrlist_setupvattr_all(alp, vap, obj_vtype,
-           attrs_fixed_sizep, IS_64BIT_PROCESS(vfs_context_proc(ctx))));
+       VATTR_INIT(vap);
+
+       // 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)), 1);
 }
 
 
@@ -721,27 +803,27 @@ 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;
 
        if (asp->commonattr) {
                tab = getattrlist_common_tab;
                do {
-            /* 
+                       /*
                         * This if() statement is slightly confusing. We're trying to
-                        * iterate through all of the bits listed in the array 
+                        * iterate through all of the bits listed in the array
                         * getattr_common_tab, and see if the filesystem was expected
                         * to support it, and whether or not we need to do anything about this.
-                        * 
+                        *
                         * This array is full of structs that have 4 fields (attr, bits, size, action).
-                        * The first is used to store the ATTR_CMN_* bit that was being requested 
+                        * The first is used to store the ATTR_CMN_* bit that was being requested
                         * from userland.  The second stores the VATTR_BIT corresponding to the field
                         * filled in vnode_attr struct.  If it is 0, then we don't typically expect
                         * the filesystem to fill in this field.  The third is the size of the field,
                         * and the fourth is the type of kauth actions needed.
                         *
-                        * So, for all of the ATTR_CMN bits listed in this array, we iterate through 
+                        * So, for all of the ATTR_CMN bits listed in this array, we iterate through
                         * them, and check to see if it was both passed down to the filesystem via the
                         * va_active bitfield, and whether or not we expect it to be emitted from
                         * the filesystem.  If it wasn't supported, then we un-twiddle the bit and move
@@ -775,14 +857,24 @@ 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
 setattrlist_setfinderinfo(vnode_t vp, char *fndrinfo, struct vfs_context *ctx)
 {
-       uio_t   auio;
-       char    uio_buf[UIO_SIZEOF(1)];
-       int     error;
+       uio_t   auio;
+       char    uio_buf[UIO_SIZEOF(1)];
+       int     error;
 
        if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_WRITE, uio_buf, sizeof(uio_buf))) == NULL) {
                error = ENOMEM;
@@ -794,10 +886,10 @@ setattrlist_setfinderinfo(vnode_t vp, char *fndrinfo, struct vfs_context *ctx)
 
 #if CONFIG_FSE
        if (error == 0 && need_fsevent(FSE_FINDER_INFO_CHANGED, vp)) {
-           add_fsevent(FSE_FINDER_INFO_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
+               add_fsevent(FSE_FINDER_INFO_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
        }
 #endif
-       return (error);
+       return error;
 }
 
 
@@ -808,8 +900,8 @@ setattrlist_setfinderinfo(vnode_t vp, char *fndrinfo, struct vfs_context *ctx)
 static void
 getattrlist_findnamecomp(const char *mn, const char **np, ssize_t *nl)
 {
-       int             counting;
-       const char      *cp;
+       int             counting;
+       const char      *cp;
 
        /*
         * We're looking for the last sequence of non / characters, but
@@ -834,27 +926,29 @@ getattrlist_findnamecomp(const char *mn, const char **np, ssize_t *nl)
                }
        }
        /* need to close run? */
-       if (counting)
+       if (counting) {
                *nl = cp - *np;
+       }
 }
 
 
 static int
 getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
-               user_addr_t attributeBuffer, size_t bufferSize, uint64_t options,
-               enum uio_seg segflg, int is_64bit)
+    user_addr_t attributeBuffer, size_t bufferSize, uint64_t options,
+    enum uio_seg segflg, int is_64bit)
 {
        struct vfs_attr vs;
        struct vnode_attr va;
        struct _attrlist_buf ab;
-       int             error;
-       ssize_t         fixedsize, varsize;
-       const char      *cnp = NULL;    /* protected by ATTR_CMN_NAME */
-       ssize_t         cnl = 0;        /* protected by ATTR_CMN_NAME */
-       int             release_str = 0;
-       mount_t         mnt;
-       int             return_valid;
-       int             pack_invalid;
+       int             error;
+       ssize_t         fixedsize, varsize;
+       const char      *cnp = NULL;    /* protected by ATTR_CMN_NAME */
+       ssize_t         cnl = 0;        /* protected by ATTR_CMN_NAME */
+       int             release_str = 0;
+       mount_t         mnt;
+       int             return_valid;
+       int             pack_invalid;
+       vnode_t         root_vp = NULL;
 
        ab.base = NULL;
        VATTR_INIT(&va);
@@ -862,7 +956,7 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
        vs.f_vol_name = NULL;
        mnt = vp->v_mount;
 
-               
+
        /* Check for special packing semantics */
        return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS);
        pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS);
@@ -873,20 +967,25 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                        goto out;
                }
                /* Keep invalid attrs from being uninitialized */
-               bzero(&vs, sizeof (vs));
+               bzero(&vs, sizeof(vs));
                /* Generate a valid mask for post processing */
-               bcopy(&alp->commonattr, &ab.valid, sizeof (attribute_set_t));
+               bcopy(&alp->commonattr, &ab.valid, sizeof(attribute_set_t));
        }
 
-       /*
-        * For now, the vnode must be the root of its filesystem.
-        * To relax this, we need to be able to find the root vnode of a filesystem
-        * from any vnode in the filesystem.
-        */
+       /* If we do not have root vnode, look it up and substitute it in */
        if (!vnode_isvroot(vp)) {
-               error = EINVAL;
-               VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
-               goto out;
+               if (mnt != NULL) {
+                       error = VFS_ROOT(mnt, &root_vp, ctx);
+                       if (error) {
+                               VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested on non-root vnode, but got an error getting root.");
+                               goto out;
+                       }
+                       vp = root_vp;
+               } else {
+                       error = EINVAL;
+                       VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested on non-root vnode, but no backpointer to mount.");
+                       goto out;
+               }
        }
 
        /*
@@ -899,12 +998,8 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
        if (vs.f_active != 0) {
                /* If we're going to ask for f_vol_name, allocate a buffer to point it at */
                if (VFSATTR_IS_ACTIVE(&vs, f_vol_name)) {
-                       vs.f_vol_name = (char *) kalloc(MAXPATHLEN);
-                       if (vs.f_vol_name == NULL) {
-                               error = ENOMEM;
-                               VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate f_vol_name buffer");
-                               goto out;
-                       }
+                       vs.f_vol_name = (char *) zalloc(ZV_NAMEI);
+                       vs.f_vol_name[0] = '\0';
                }
 
                VFS_DEBUG(ctx, vp, "ATTRLIST -       calling to get %016llx with supported %016llx", vs.f_active, vs.f_supported);
@@ -925,8 +1020,9 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                if (!VFSATTR_ALL_SUPPORTED(&vs)) {
                        /* default value for volume subtype */
                        if (VFSATTR_IS_ACTIVE(&vs, f_fssubtype)
-                           && !VFSATTR_IS_SUPPORTED(&vs, f_fssubtype))
+                           && !VFSATTR_IS_SUPPORTED(&vs, f_fssubtype)) {
                                VFSATTR_RETURN(&vs, f_fssubtype, 0);
+                       }
 
                        /*
                         * If the file system didn't supply f_signature, then
@@ -934,25 +1030,27 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                         * that most Carbon file systems should return.
                         */
                        if (VFSATTR_IS_ACTIVE(&vs, f_signature)
-                           && !VFSATTR_IS_SUPPORTED(&vs, f_signature))
+                           && !VFSATTR_IS_SUPPORTED(&vs, f_signature)) {
                                VFSATTR_RETURN(&vs, f_signature, 0x4244);
+                       }
 
                        /* default for block size */
                        if (VFSATTR_IS_ACTIVE(&vs, f_bsize)
-                           && !VFSATTR_IS_SUPPORTED(&vs, f_bsize))
+                           && !VFSATTR_IS_SUPPORTED(&vs, f_bsize)) {
                                VFSATTR_RETURN(&vs, f_bsize, mnt->mnt_devblocksize);
+                       }
 
                        /* default value for volume f_attributes */
                        if (VFSATTR_IS_ACTIVE(&vs, f_attributes)
                            && !VFSATTR_IS_SUPPORTED(&vs, f_attributes)) {
                                vol_attributes_attr_t *attrp = &vs.f_attributes;
-               
+
                                attrp->validattr.commonattr = VFS_DFLT_ATTR_CMN;
                                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;
                                attrp->nativeattr.dirattr = 0;
@@ -969,14 +1067,13 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                                        vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST;
                                        vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
                                        vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
-       
+
                                        vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] = 0;
                                        vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST;
                                        vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0;
                                        vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0;
                                        VFSATTR_SET_SUPPORTED(&vs, f_capabilities);
-                               }
-                               else {
+                               } else {
                                        /* OR in VOL_CAP_INT_ATTRLIST if f_capabilities is supported */
                                        vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST;
                                        vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST;
@@ -989,7 +1086,7 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                                        if (pack_invalid) {
                                                /* Fix up valid mask for post processing */
                                                getvolattrlist_fixupattrs(&ab.valid, &vs);
-                                               
+
                                                /* Force packing of everything asked for */
                                                vs.f_supported = vs.f_active;
                                        } else {
@@ -1027,12 +1124,13 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
 #endif
                if (VATTR_IS_ACTIVE(&va, va_encoding) &&
                    !VATTR_IS_SUPPORTED(&va, va_encoding)) {
-                       if (!return_valid || pack_invalid)
+                       if (!return_valid || pack_invalid) {
                                /* use kTextEncodingMacUnicode */
                                VATTR_RETURN(&va, va_encoding, 0x7e);
-                       else
+                       } else {
                                /* don't use a default */
                                alp->commonattr &= ~ATTR_CMN_SCRIPT;
+                       }
                }
        }
 
@@ -1042,7 +1140,7 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
        varsize = 0;
        if (alp->commonattr & ATTR_CMN_NAME) {
                if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
-                       vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
+                   vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
                        /* special case for boot volume.  Use root name when it's
                         * available (which is the volume name) or just the mount on
                         * name of "/".  we must do this for binary compatibility with
@@ -1053,39 +1151,61 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                        if (cnp == NULL) {
                                /* just use "/" as name */
                                cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
-                       }
-                       else {
+                       } else {
                                release_str = 1;
                        }
                        cnl = strlen(cnp);
-               }
-               else {
+               } else {
                        getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
                }
-               if (alp->commonattr & ATTR_CMN_NAME)
+               if (alp->commonattr & ATTR_CMN_NAME) {
                        varsize += roundup(cnl + 1, 4);
+               }
        }
-       if (alp->volattr & ATTR_VOL_MOUNTPOINT)
+       if (alp->volattr & ATTR_VOL_MOUNTPOINT) {
                varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntonname) + 1, 4);
+       }
        if (alp->volattr & ATTR_VOL_NAME) {
-               vs.f_vol_name[MAXPATHLEN-1] = '\0'; /* Ensure nul-termination */
+               vs.f_vol_name[MAXPATHLEN - 1] = '\0'; /* Ensure nul-termination */
                varsize += roundup(strlen(vs.f_vol_name) + 1, 4);
        }
-       if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE)
+       if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE) {
                varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntfromname) + 1, 4);
+       }
 
        /*
         * Allocate a target buffer for attribute results.
         * 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;
        }
-       MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_ZERO | M_WAITOK);
+
+       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;
+       }
+
+       ab.base = kheap_alloc(KHEAP_TEMP, ab.allocated, Z_ZERO | Z_WAITOK);
        if (ab.base == NULL) {
                error = ENOMEM;
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
@@ -1097,13 +1217,17 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
         */
        ab.fixedcursor = ab.base + sizeof(uint32_t);
        if (return_valid) {
-               ab.fixedcursor += sizeof (attribute_set_t);
-               bzero(&ab.actual, sizeof (ab.actual));
+               ab.fixedcursor += sizeof(attribute_set_t);
+               bzero(&ab.actual, sizeof(ab.actual));
        }
        ab.varcursor = ab.base + fixedsize;
        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;
@@ -1117,8 +1241,9 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                ab.actual.commonattr |= ATTR_CMN_FSID;
        }
        if (alp->commonattr & ATTR_CMN_OBJTYPE) {
-               if (!return_valid || pack_invalid)
+               if (!return_valid || pack_invalid) {
                        ATTR_PACK4(ab, 0);
+               }
        }
        if (alp->commonattr & ATTR_CMN_OBJTAG) {
                ATTR_PACK4(ab, vp->v_tag);
@@ -1156,8 +1281,9 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                ab.actual.commonattr |= ATTR_CMN_MODTIME;
        }
        if (alp->commonattr & ATTR_CMN_CHGTIME) {
-               if (!return_valid || pack_invalid)
+               if (!return_valid || pack_invalid) {
                        ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
+               }
        }
        if (alp->commonattr & ATTR_CMN_ACCTIME) {
                ATTR_PACK_TIME(ab, vs.f_access_time, is_64bit);
@@ -1203,39 +1329,51 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                ATTR_PACK4(ab, va.va_flags);
                ab.actual.commonattr |= ATTR_CMN_FLAGS;
        }
-       if (alp->commonattr & ATTR_CMN_USERACCESS) {    /* XXX this is expensive and also duplicate work */
-               uint32_t        perms = 0;
+       if (alp->commonattr & ATTR_CMN_USERACCESS) {    /* XXX this is expensive and also duplicate work */
+               uint32_t        perms = 0;
                if (vnode_isdir(vp)) {
                        if (vnode_authorize(vp, NULL,
-                               KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
+                           KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0) {
                                perms |= W_OK;
-                       if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
+                       }
+                       if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0) {
                                perms |= R_OK;
-                       if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0)
+                       }
+                       if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0) {
                                perms |= X_OK;
+                       }
                } else {
-                       if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0)
+                       if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0) {
                                perms |= W_OK;
-                       if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
+                       }
+                       if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0) {
                                perms |= R_OK;
-                       if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
+                       }
+                       if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0) {
                                perms |= X_OK;
+                       }
                }
 #if CONFIG_MACF
-               /* 
+               /*
                 * Rather than MAC preceding DAC, in this case we want
                 * the smallest set of permissions granted by both MAC & DAC
                 * checks.  We won't add back any permissions.
                 */
-               if (perms & W_OK)
-                       if (mac_vnode_check_access(ctx, vp, W_OK) != 0)
+               if (perms & W_OK) {
+                       if (mac_vnode_check_access(ctx, vp, W_OK) != 0) {
                                perms &= ~W_OK;
-               if (perms & R_OK)
-                       if (mac_vnode_check_access(ctx, vp, R_OK) != 0)
+                       }
+               }
+               if (perms & R_OK) {
+                       if (mac_vnode_check_access(ctx, vp, R_OK) != 0) {
                                perms &= ~R_OK;
-               if (perms & X_OK)
-                       if (mac_vnode_check_access(ctx, vp, X_OK) != 0)
+                       }
+               }
+               if (perms & X_OK) {
+                       if (mac_vnode_check_access(ctx, vp, X_OK) != 0) {
                                perms &= ~X_OK;
+                       }
+               }
 #endif /* MAC */
                KAUTH_DEBUG("ATTRLIST - returning user access %x", perms);
                ATTR_PACK4(ab, perms);
@@ -1248,16 +1386,21 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
        if (pack_invalid) {
                uint64_t fid = 0;
 
-               if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY)
+               if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY) {
                        attrlist_pack_variable(&ab, NULL, 0);
-               if (alp->commonattr & ATTR_CMN_UUID)
+               }
+               if (alp->commonattr & ATTR_CMN_UUID) {
                        ATTR_PACK(&ab, kauth_null_guid);
-               if (alp->commonattr & ATTR_CMN_GRPUUID)
+               }
+               if (alp->commonattr & ATTR_CMN_GRPUUID) {
                        ATTR_PACK(&ab, kauth_null_guid);
-               if (alp->commonattr & ATTR_CMN_FILEID)
+               }
+               if (alp->commonattr & ATTR_CMN_FILEID) {
                        ATTR_PACK8(ab, fid);
-               if (alp->commonattr & ATTR_CMN_PARENTID)
+               }
+               if (alp->commonattr & ATTR_CMN_PARENTID) {
                        ATTR_PACK8(ab, fid);
+               }
        }
 
        /* volume attributes **************************************************/
@@ -1266,8 +1409,8 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                ATTR_PACK_CAST(&ab, uint32_t, vfs_typenum(mnt));
                ab.actual.volattr |= ATTR_VOL_FSTYPE;
        }
-       if (alp->volattr & ATTR_VOL_SIGNATURE) {
-               ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature);
+       if (alp->volattr & ATTR_VOL_SIGNATURE) {
+               ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature);
                ab.actual.volattr |= ATTR_VOL_SIGNATURE;
        }
        if (alp->volattr & ATTR_VOL_SIZE) {
@@ -1287,7 +1430,7 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                ab.actual.volattr |= ATTR_VOL_MINALLOCATION;
        }
        if (alp->volattr & ATTR_VOL_ALLOCATIONCLUMP) {
-               ATTR_PACK_CAST(&ab, off_t, vs.f_bsize);                 /* not strictly true */
+               ATTR_PACK_CAST(&ab, off_t, vs.f_bsize);                 /* not strictly true */
                ab.actual.volattr |= ATTR_VOL_ALLOCATIONCLUMP;
        }
        if (alp->volattr & ATTR_VOL_IOBLOCKSIZE) {
@@ -1327,8 +1470,9 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                ab.actual.volattr |= ATTR_VOL_MOUNTEDDEVICE;
        }
        if (alp->volattr & ATTR_VOL_ENCODINGSUSED) {
-               if (!return_valid || pack_invalid)
+               if (!return_valid || pack_invalid) {
                        ATTR_PACK_CAST(&ab, uint64_t, ~0LL);  /* return all encodings */
+               }
        }
        if (alp->volattr & ATTR_VOL_CAPABILITIES) {
                /* fix up volume capabilities */
@@ -1338,6 +1482,29 @@ 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_CMN_USERACCESS attribute was previously set by file-system drivers, thus volume capabilitiy
+                * VOL_CAP_INT_USERACCESS was conditionally enabled. ATTR_CMN_USERACCESS is now set inside VFS,
+                * regardless of underlying volume type thus we always set VOL_CAP_INT_USERACCESS.
+                */
+               vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_USERACCESS;
+               vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_USERACCESS;
+
                ATTR_PACK(&ab, vs.f_capabilities);
                ab.actual.volattr |= ATTR_VOL_CAPABILITIES;
        }
@@ -1345,6 +1512,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 */
 
@@ -1362,13 +1537,15 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
                ATTR_PACK(&ab, vs.f_attributes);
                ab.actual.volattr |= ATTR_VOL_ATTRIBUTES;
        }
-       
+
        /* diagnostic */
-       if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize)
+       if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize) {
                panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
                    fixedsize, (long) (ab.fixedcursor - ab.base), alp->commonattr, alp->volattr);
-       if (!return_valid && ab.varcursor != (ab.base + ab.needed))
+       }
+       if (!return_valid && ab.varcursor != (ab.base + ab.needed)) {
                panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab.varcursor - ab.base), ab.needed);
+       }
 
        /*
         * In the compatible case, we report the smaller of the required and returned sizes.
@@ -1376,35 +1553,41 @@ 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) ? (uint32_t)ab.needed : (uint32_t)lmin(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 */
                        ab.actual.commonattr &= ab.valid.commonattr;
                        ab.actual.volattr &= ab.valid.volattr;
                }
-               bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual));
+               bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof(ab.actual));
        }
 
-       if (UIO_SEG_IS_USER_SPACE(segflg))
+       if (UIO_SEG_IS_USER_SPACE(segflg)) {
                error = copyout(ab.base, CAST_USER_ADDR_T(attributeBuffer),
-                               ab.allocated);
-       else
-               bcopy(ab.base, (void *)attributeBuffer, (size_t)ab.allocated);
+                   ulmin((uint32_t)bufferSize, (uint32_t)ab.needed));
+       } else {
+               bcopy(ab.base, (void *)attributeBuffer, (size_t)ulmin((uint32_t)bufferSize, (uint32_t)ab.needed));
+       }
 
 out:
-       if (vs.f_vol_name != NULL)
-               kfree(vs.f_vol_name, MAXPATHLEN);
+       if (vs.f_vol_name != NULL) {
+               zfree(ZV_NAMEI, vs.f_vol_name);
+       }
        if (release_str) {
                vnode_putname(cnp);
        }
-       if (ab.base != NULL)
-               FREE(ab.base, M_TEMP);
+       kheap_free(KHEAP_TEMP, ab.base, ab.allocated);
        VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
-       return(error);
+
+       if (root_vp != NULL) {
+               vnode_put(root_vp);
+       }
+       return error;
 }
 
 /*
@@ -1415,14 +1598,14 @@ out:
  * are in ad.
  */
 static errno_t
-attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
+attr_pack_common(vfs_context_t ctx, mount_t mp, vnode_t vp, struct attrlist *alp,
     struct _attrlist_buf *abp, struct vnode_attr *vap, int proc_is64,
     const char *cnp, ssize_t cnl, const char *fullpathptr,
     ssize_t fullpathlen, int return_valid, int pack_invalid, int vtype,
     int is_bulk)
 {
-       uint32_t        perms = 0;
-       int             error = 0;
+       uint32_t        perms = 0;
+       int             error = 0;
 
        if ((alp->commonattr & ATTR_CMN_ERROR) &&
            (!return_valid || pack_invalid)) {
@@ -1434,7 +1617,14 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                abp->actual.commonattr |= ATTR_CMN_NAME;
        }
        if (alp->commonattr & ATTR_CMN_DEVID) {
-               if (vp) {
+               if (mp) { /* caller needs real devid */
+                       ATTR_PACK4((*abp),
+                           mp->mnt_vfsstat.f_fsid.val[0]);
+                       abp->actual.commonattr |= ATTR_CMN_DEVID;
+               } else if (VATTR_IS_ACTIVE(vap, va_fsid) && VATTR_IS_SUPPORTED(vap, va_fsid)) {
+                       ATTR_PACK4((*abp), vap->va_fsid);
+                       abp->actual.commonattr |= ATTR_CMN_DEVID;
+               } else if (vp) {
                        ATTR_PACK4((*abp),
                            vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
                        abp->actual.commonattr |= ATTR_CMN_DEVID;
@@ -1446,16 +1636,19 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                }
        }
        if (alp->commonattr & ATTR_CMN_FSID) {
-               if (vp) {
+               if (mp) { /* caller needs real fsid */
                        ATTR_PACK8((*abp),
-                           vp->v_mount->mnt_vfsstat.f_fsid);
+                           mp->mnt_vfsstat.f_fsid);
                        abp->actual.commonattr |= ATTR_CMN_FSID;
                } else if (VATTR_IS_SUPPORTED(vap, va_fsid64)) {
                        ATTR_PACK8((*abp), vap->va_fsid64);
                        abp->actual.commonattr |= ATTR_CMN_FSID;
+               } else if (vp) {
+                       ATTR_PACK8((*abp),
+                           vp->v_mount->mnt_vfsstat.f_fsid);
+                       abp->actual.commonattr |= ATTR_CMN_FSID;
                } else if (!return_valid || pack_invalid) {
                        fsid_t fsid = {{0}};
-
                        ATTR_PACK8((*abp), fsid);
                }
        }
@@ -1490,9 +1683,9 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                 */
                if (vap->va_vaflags & VA_64BITOBJIDS) {
                        if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
-                               ATTR_PACK8((*abp),  vap->va_linkid);
+                               ATTR_PACK8((*abp), vap->va_linkid);
                        } else {
-                               ATTR_PACK8((*abp),  vap->va_fileid);
+                               ATTR_PACK8((*abp), vap->va_fileid);
                        }
                } else {
                        fsobj_id_t f;
@@ -1515,9 +1708,9 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                 */
                if (vap->va_vaflags & VA_64BITOBJIDS) {
                        if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
-                               ATTR_PACK8((*abp),  vap->va_linkid);
+                               ATTR_PACK8((*abp), vap->va_linkid);
                        } else {
-                               ATTR_PACK8((*abp),  vap->va_fileid);
+                               ATTR_PACK8((*abp), vap->va_fileid);
                        }
                } else {
                        fsobj_id_t f;
@@ -1543,7 +1736,7 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                abp->actual.commonattr |= ATTR_CMN_PAROBJID;
        }
        if (alp->commonattr & ATTR_CMN_SCRIPT) {
-               if (VATTR_IS_SUPPORTED(vap, va_encoding)) {
+               if (VATTR_IS_SUPPORTED(vap, va_encoding)) {
                        ATTR_PACK4((*abp), vap->va_encoding);
                        abp->actual.commonattr |= ATTR_CMN_SCRIPT;
                } else if (!return_valid || pack_invalid) {
@@ -1571,36 +1764,42 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                abp->actual.commonattr |= ATTR_CMN_BKUPTIME;
        }
        /*
-        * They are requesting user access, we should obtain this before getting 
+        * They are requesting user access, we should obtain this before getting
         * the finder info. For some network file systems this is a performance
         * improvement.
         */
-       if (alp->commonattr & ATTR_CMN_USERACCESS) {    /* this is expensive */
+       if (alp->commonattr & ATTR_CMN_USERACCESS) {    /* this is expensive */
                if (vp && !is_bulk) {
                        if (vtype == VDIR) {
                                if (vnode_authorize(vp, NULL,
                                    KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE |
                                    KAUTH_VNODE_ADD_SUBDIRECTORY |
-                                   KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
+                                   KAUTH_VNODE_DELETE_CHILD, ctx) == 0) {
                                        perms |= W_OK;
+                               }
 
                                if (vnode_authorize(vp, NULL,
                                    KAUTH_VNODE_ACCESS |
-                                   KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
+                                   KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0) {
                                        perms |= R_OK;
+                               }
 
                                if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS |
-                                   KAUTH_VNODE_SEARCH, ctx) == 0)
+                                   KAUTH_VNODE_SEARCH, ctx) == 0) {
                                        perms |= X_OK;
+                               }
                        } else {
                                if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS |
-                                   KAUTH_VNODE_WRITE_DATA, ctx) == 0)
+                                   KAUTH_VNODE_WRITE_DATA, ctx) == 0) {
                                        perms |= W_OK;
+                               }
 
-                               if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
+                               if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0) {
                                        perms |= R_OK;
-                               if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
+                               }
+                               if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0) {
                                        perms |= X_OK;
+                               }
                        }
                } else if (is_bulk &&
                    VATTR_IS_SUPPORTED(vap, va_user_access)) {
@@ -1608,12 +1807,12 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                }
        }
        if (alp->commonattr & ATTR_CMN_FNDRINFO) {
-               size_t  fisize = 32;
+               size_t  fisize = 32;
 
-               error = 0; 
+               error = 0;
                if (vp && !is_bulk) {
-                       uio_t   auio;
-                       char    uio_buf[UIO_SIZEOF(1)];
+                       uio_t   auio;
+                       char    uio_buf[UIO_SIZEOF(1)];
 
                        if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE,
                            UIO_READ, uio_buf, sizeof(uio_buf))) == NULL) {
@@ -1624,7 +1823,7 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                            fisize);
                        /* fisize may be reset to 0 after this call */
                        error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio,
-                                           &fisize, XATTR_NOSECURITY, ctx);
+                           &fisize, XATTR_NOSECURITY, ctx);
                        uio_free(auio);
 
                        /*
@@ -1693,7 +1892,7 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                        abp->actual.commonattr |= ATTR_CMN_DOCUMENT_ID;
                } else if (!return_valid || pack_invalid) {
                        ATTR_PACK4((*abp), 0);
-               }       
+               }
        }
        /* We already obtain the user access, so just fill in the buffer here */
        if (alp->commonattr & ATTR_CMN_USERACCESS) {
@@ -1704,15 +1903,21 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                         * the smallest set of permissions granted by both MAC &
                         * DAC checks.  We won't add back any permissions.
                         */
-                       if (perms & W_OK)
-                               if (mac_vnode_check_access(ctx, vp, W_OK) != 0)
+                       if (perms & W_OK) {
+                               if (mac_vnode_check_access(ctx, vp, W_OK) != 0) {
                                        perms &= ~W_OK;
-                       if (perms & R_OK)
-                               if (mac_vnode_check_access(ctx, vp, R_OK) != 0)
+                               }
+                       }
+                       if (perms & R_OK) {
+                               if (mac_vnode_check_access(ctx, vp, R_OK) != 0) {
                                        perms &= ~R_OK;
-                       if (perms & X_OK)
-                               if (mac_vnode_check_access(ctx, vp, X_OK) != 0)
+                               }
+                       }
+                       if (perms & X_OK) {
+                               if (mac_vnode_check_access(ctx, vp, X_OK) != 0) {
                                        perms &= ~X_OK;
+                               }
+                       }
                }
 #endif /* MAC */
                VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d", perms);
@@ -1742,7 +1947,7 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                }
        }
        if (alp->commonattr & ATTR_CMN_UUID) {
-               if (VATTR_IS_SUPPORTED(vap, va_uuuid)) {
+               if (VATTR_IS_SUPPORTED(vap, va_uuuid)) {
                        ATTR_PACK(abp, vap->va_uuuid);
                        abp->actual.commonattr |= ATTR_CMN_UUID;
                } else if (!return_valid || pack_invalid) {
@@ -1765,20 +1970,20 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                ATTR_PACK8((*abp), vap->va_parentid);
                abp->actual.commonattr |= ATTR_CMN_PARENTID;
        }
-       
+
        if (alp->commonattr & ATTR_CMN_FULLPATH) {
                if (vp) {
-                       attrlist_pack_string (abp, fullpathptr, fullpathlen);
+                       attrlist_pack_string(abp, fullpathptr, fullpathlen);
                        abp->actual.commonattr |= ATTR_CMN_FULLPATH;
                }
        }
-    
+
        if (alp->commonattr & ATTR_CMN_ADDEDTIME) {
                if (VATTR_IS_SUPPORTED(vap, va_addedtime)) {
                        ATTR_PACK_TIME((*abp), vap->va_addedtime, proc_is64);
                        abp->actual.commonattr |= ATTR_CMN_ADDEDTIME;
                } else if (!return_valid || pack_invalid) {
-                       struct timespec zerotime = {0, 0};
+                       struct timespec zerotime = {.tv_sec = 0, .tv_nsec = 0};
 
                        ATTR_PACK_TIME((*abp), zerotime, proc_is64);
                }
@@ -1792,12 +1997,12 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                }
        }
 out:
-       return (error);
+       return error;
 }
 
 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);
@@ -1817,7 +2022,7 @@ attr_pack_dir(struct vnode *vp, struct attrlist *alp, struct _attrlist_buf *abp,
                         * on vnode. In either case, the directory should
                         * be reported as a mount point.
                         */
-                       if ((vp->v_flag & VROOT) ||  vnode_mountedhere(vp)) {
+                       if ((vp->v_flag & VROOT) || vnode_mountedhere(vp)) {
                                mntstat = DIR_MNTSTATUS_MNTPOINT;
                        } else {
                                mntstat = 0;
@@ -1839,6 +2044,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;
 }
@@ -1850,11 +2092,11 @@ attr_pack_dir(struct vnode *vp, struct attrlist *alp, struct _attrlist_buf *abp,
  * attempt is made to retrieve them by calling back into the filesystem.
  */
 static errno_t
-attr_pack_file(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
+attr_pack_file(vfs_context_t ctx, struct vnode *vp, struct attrlist *alp,
     struct _attrlist_buf *abp, struct vnode_attr *vap, int return_valid,
     int pack_invalid, int is_bulk)
 {
-       size_t  rsize = 0;
+       size_t  rsize = 0;
        uint64_t rlength = 0;
        uint64_t ralloc = 0;
        int error = 0;
@@ -1866,7 +2108,6 @@ attr_pack_file(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
        if (vp && !is_bulk &&
            (alp->fileattr & (ATTR_FILE_TOTALSIZE | ATTR_FILE_ALLOCSIZE |
            ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE))) {
-
                error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL,
                    &rsize, XATTR_NOSECURITY, ctx);
                if (error) {
@@ -1919,7 +2160,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;
                        }
 
@@ -1944,7 +2185,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;
                        }
 
@@ -1960,8 +2201,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) {
@@ -2000,18 +2245,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) {
@@ -2040,13 +2293,132 @@ attr_pack_file(vfs_context_t ctx, struct vnode *vp,  struct attrlist *alp,
                        ATTR_PACK8((*abp), zero_val);
                }
        }
-out:
-       return (error);
+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(mount_t mp, struct vnode *vp, struct attrlist *alp,
+    struct _attrlist_buf *abp, const char *relpathptr, ssize_t relpathlen,
+    const char *REALpathptr, ssize_t REALpathlen,
+    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;
+       }
+
+       if (vp && (alp->forkattr & ATTR_CMNEXT_NOFIRMLINKPATH)) {
+               attrlist_pack_string(abp, REALpathptr, REALpathlen);
+               abp->actual.forkattr |= ATTR_CMNEXT_NOFIRMLINKPATH;
+       }
+
+       if (alp->forkattr & ATTR_CMNEXT_REALDEVID) {
+               if (mp) {
+                       ATTR_PACK4((*abp),
+                           mp->mnt_vfsstat.f_fsid.val[0]);
+                       abp->actual.forkattr |= ATTR_CMNEXT_REALDEVID;
+               } else if (vp) {
+                       ATTR_PACK4((*abp),
+                           vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
+                       abp->actual.forkattr |= ATTR_CMNEXT_REALDEVID;
+               } else if (VATTR_IS_SUPPORTED(vap, va_fsid)) {
+                       ATTR_PACK4((*abp), vap->va_fsid);
+                       abp->actual.forkattr |= ATTR_CMN_DEVID;
+               } else if (!return_valid || pack_invalid) {
+                       ATTR_PACK4((*abp), 0);
+               }
+       }
+
+       if (alp->forkattr & ATTR_CMNEXT_REALFSID) {
+               if (mp) {
+                       ATTR_PACK8((*abp),
+                           mp->mnt_vfsstat.f_fsid);
+                       abp->actual.forkattr |= ATTR_CMNEXT_REALFSID;
+               } else if (vp) {
+                       ATTR_PACK8((*abp),
+                           vp->v_mount->mnt_vfsstat.f_fsid);
+                       abp->actual.forkattr |= ATTR_CMNEXT_REALFSID;
+               } else if (VATTR_IS_SUPPORTED(vap, va_fsid64)) {
+                       ATTR_PACK8((*abp), vap->va_fsid64);
+                       abp->actual.forkattr |= ATTR_CMN_FSID;
+               } else if (!return_valid || pack_invalid) {
+                       fsid_t fsid = {{0}};
+
+                       ATTR_PACK8((*abp), fsid);
+               }
+       }
+
+       if (alp->forkattr & ATTR_CMNEXT_CLONEID) {
+               if (VATTR_IS_SUPPORTED(vap, va_clone_id)) {
+                       ATTR_PACK8((*abp), vap->va_clone_id);
+                       abp->actual.forkattr |= ATTR_CMNEXT_CLONEID;
+               } else if (!return_valid || pack_invalid) {
+                       uint64_t zero_val = 0;
+                       ATTR_PACK8((*abp), zero_val);
+               }
+       }
+
+       if (alp->forkattr & ATTR_CMNEXT_EXT_FLAGS) {
+               if (VATTR_IS_SUPPORTED(vap, va_extflags)) {
+                       ATTR_PACK8((*abp), vap->va_extflags);
+                       abp->actual.forkattr |= ATTR_CMNEXT_EXT_FLAGS;
+               } else if (!return_valid || pack_invalid) {
+                       uint64_t zero_val = 0;
+                       ATTR_PACK8((*abp), zero_val);
+               }
+       }
+
+       if (alp->forkattr & ATTR_CMNEXT_RECURSIVE_GENCOUNT) {
+               if (VATTR_IS_SUPPORTED(vap, va_recursive_gencount)) {
+                       ATTR_PACK8((*abp), vap->va_recursive_gencount);
+                       abp->actual.forkattr |= ATTR_CMNEXT_RECURSIVE_GENCOUNT;
+               } else if (!return_valid || pack_invalid) {
+                       uint64_t zero_val = 0;
+                       ATTR_PACK8((*abp), zero_val);
+               }
+       }
+
+       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)
+    int return_valid, int is_bulk,
+#if !CONFIG_FIRMLINKS
+    __unused
+#endif
+    int is_realdev, vfs_context_t ctx)
 {
        /*
         * There are a couple of special cases.
@@ -2058,28 +2430,67 @@ vattr_get_alt_data(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
                /* forget we wanted this */
                VATTR_CLEAR_ACTIVE(vap, va_linkid);
        }
-       
+
+       /*
+        * A filesystem may not support va_fsid64.  If it is not available, then we'll
+        * synthesize it from the mount.
+        */
+       if ((alp->commonattr & ATTR_CMN_FSID) && !VATTR_IS_SUPPORTED(vap, va_fsid64)) {
+               VATTR_CLEAR_ACTIVE(vap, va_fsid64);
+       }
+
+       /* Same for fsid */
+       if ((alp->commonattr & ATTR_CMN_FSID) && !VATTR_IS_SUPPORTED(vap, va_fsid)) {
+               VATTR_CLEAR_ACTIVE(vap, va_fsid);
+       }
+
+       /* We request the fsid64 for the devid */
+       if ((alp->commonattr & ATTR_CMN_DEVID) && !VATTR_IS_SUPPORTED(vap, va_fsid)) {
+               VATTR_CLEAR_ACTIVE(vap, va_fsid);
+       }
+
+
        /*
         * Many filesystems don't know their parent object id.
         * If necessary, attempt to derive it from the vnode.
         */
-       if ((alp->commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)) &&
-           !VATTR_IS_SUPPORTED(vap, va_parentid) && vp && !is_bulk) {
-               vnode_t dvp;
+       if ((alp->commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)) && vp) {
+               vnode_t dvp;
 
-               if ((dvp = vnode_getparent(vp)) != NULLVP) {
+#if CONFIG_FIRMLINKS
+               /* If this is a firmlink target, we get the fileid of the firmlink parent. */
+               if (!is_realdev && (vp->v_flag & VFMLINKTARGET) && ((dvp = vp->v_fmlink) != NULL) && (vnode_get(dvp) == 0)) {
                        struct vnode_attr lva;
 
                        VATTR_INIT(&lva);
-                       VATTR_WANTED(&lva, va_fileid);
+                       VATTR_WANTED(&lva, va_parentid);
+                       VATTR_WANTED(&lva, va_fsid);
                        if (vnode_getattr(dvp, &lva, ctx) == 0 &&
-                           VATTR_IS_SUPPORTED(vap, va_fileid)) {
-                               vap->va_parentid = lva.va_fileid;
+                           VATTR_IS_SUPPORTED(&lva, va_parentid) &&
+                           VATTR_IS_SUPPORTED(&lva, va_fsid) &&
+                           (lva.va_fsid == (uint32_t)vp->v_mount->mnt_vfsstat.f_fsid.val[0])) {
+                               vap->va_parentid = lva.va_parentid;
                                VATTR_SET_SUPPORTED(vap, va_parentid);
                        }
                        vnode_put(dvp);
+               } else
+#endif /* CONFIG_FIRMLINKS */
+               if (!VATTR_IS_SUPPORTED(vap, va_parentid) && !is_bulk) {
+                       if ((dvp = vnode_getparent(vp)) != NULLVP) {
+                               struct vnode_attr lva;
+
+                               VATTR_INIT(&lva);
+                               VATTR_WANTED(&lva, va_fileid);
+                               if (vnode_getattr(dvp, &lva, ctx) == 0 &&
+                                   VATTR_IS_SUPPORTED(vap, va_fileid)) {
+                                       vap->va_parentid = lva.va_fileid;
+                                       VATTR_SET_SUPPORTED(vap, va_parentid);
+                               }
+                               vnode_put(dvp);
+                       }
                }
        }
+
        /*
         * And we can report datasize/alloc from total.
         */
@@ -2118,10 +2529,19 @@ vattr_get_alt_data(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
        }
 }
 
+struct _attrlist_paths {
+       char *fullpathptr;
+       ssize_t *fullpathlenp;
+       char *relpathptr;
+       ssize_t *relpathlenp;
+       char *REALpathptr;
+       ssize_t *REALpathlenp;
+};
+
 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)  
+    ssize_t *varsizep, struct _attrlist_paths *pathsp, const char **vnamep,
+    const char **cnpp, ssize_t *cnlp)
 {
        int error = 0;
 
@@ -2129,14 +2549,14 @@ calc_varsize(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
        /* We may need to fix up the name attribute if requested */
        if (alp->commonattr & ATTR_CMN_NAME) {
                if (VATTR_IS_SUPPORTED(vap, va_name)) {
-                       vap->va_name[MAXPATHLEN-1] = '\0';      /* Ensure nul-termination */
+                       vap->va_name[MAXPATHLEN - 1] = '\0';      /* Ensure nul-termination */
                        *cnpp = vap->va_name;
                        *cnlp = strlen(*cnpp);
                } else if (vp) {
                        /* Filesystem did not support getting the name */
                        if (vnode_isvroot(vp)) {
                                if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
-                                               vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
+                                   vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
                                        /* special case for boot volume.  Use root name when it's
                                         * available (which is the volume name) or just the mount on
                                         * name of "/".  we must do this for binary compatibility with
@@ -2149,12 +2569,10 @@ calc_varsize(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
                                                *cnpp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
                                        }
                                        *cnlp = strlen(*cnpp);
-                               }
-                               else {
+                               } else {
                                        getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, cnpp, cnlp);
                                }
-                       } 
-                       else {
+                       } else {
                                *cnpp = *vnamep = vnode_getname(vp);
                                *cnlp = 0;
                                if (*cnpp != NULL) {
@@ -2167,7 +2585,7 @@ calc_varsize(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
                *varsizep += roundup(*cnlp + 1, 4);
        }
 
-       /* 
+       /*
         * Compute the full path to this vnode, if necessary. This attribute is almost certainly
         * not supported by any filesystem, so build the path to this vnode at this time.
         */
@@ -2176,16 +2594,55 @@ calc_varsize(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
                int err;
 
                /* call build_path making sure NOT to use the cache-only behavior */
-               err = build_path(vp, fullpathptr, len, &len, 0, vfs_context_current());
+               err = build_path(vp, pathsp->fullpathptr, len, &len, 0, vfs_context_current());
+               if (err) {
+                       error = err;
+                       goto out;
+               }
+               if (pathsp->fullpathptr) {
+                       *(pathsp->fullpathlenp) = strlen(pathsp->fullpathptr);
+               } else {
+                       *(pathsp->fullpathlenp) = 0;
+               }
+               *varsizep += roundup(((*(pathsp->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, pathsp->relpathptr, MAXPATHLEN, &len, BUILDPATH_VOLUME_RELATIVE, vfs_context_current());
                if (err) {
                        error = err;
                        goto out;
                }
-               *fullpathlenp = 0;
-               if (fullpathptr){
-                       *fullpathlenp = strlen(fullpathptr);
+
+               //`len' includes trailing null
+               *(pathsp->relpathlenp) = len - 1;
+               *varsizep += roundup(len, 4);
+       }
+
+       /*
+        * Compute this vnode's real (firmlink free) path.
+        */
+       if (vp && (alp->forkattr & ATTR_CMNEXT_NOFIRMLINKPATH)) {
+               int len;
+               int err;
+
+               /* call build_path making sure NOT to use the cache-only behavior */
+               err = build_path(vp, pathsp->REALpathptr, MAXPATHLEN, &len, BUILDPATH_NO_FIRMLINK, vfs_context_current());
+               if (err) {
+                       error = err;
+                       goto out;
                }
-               *varsizep += roundup(((*fullpathlenp) + 1), 4);
+
+               //`len' includes trailing null
+               *(pathsp->REALpathlenp) = len - 1;
+               *varsizep += roundup(len, 4);
        }
 
        /*
@@ -2195,44 +2652,51 @@ calc_varsize(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
         * user-space this is OK.
         */
        if ((alp->commonattr & ATTR_CMN_EXTENDED_SECURITY) &&
-                       VATTR_IS_SUPPORTED(vap, va_acl) &&
-                       (vap->va_acl != NULL)) {
-
-               /* 
+           VATTR_IS_SUPPORTED(vap, va_acl) &&
+           (vap->va_acl != NULL)) {
+               /*
                 * Since we have a kauth_acl_t (not a kauth_filesec_t), we have to check against
                 * KAUTH_FILESEC_NOACL ourselves
-                */ 
+                */
                if (vap->va_acl->acl_entrycount == KAUTH_FILESEC_NOACL) {
                        *varsizep += roundup((KAUTH_FILESEC_SIZE(0)), 4);
-               }
-               else {
-                       *varsizep += roundup ((KAUTH_FILESEC_SIZE(vap->va_acl->acl_entrycount)), 4);
+               } else {
+                       *varsizep += roundup((KAUTH_FILESEC_SIZE(vap->va_acl->acl_entrycount)), 4);
                }
        }
 
 out:
-       return (error);
+       return error;
 }
 
 static errno_t
-vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
+vfs_attr_pack_internal(mount_t mp, vnode_t vp, uio_t auio, struct attrlist *alp,
     uint64_t options, struct vnode_attr *vap, __unused void *fndesc,
     vfs_context_t ctx, int is_bulk, enum vtype vtype, ssize_t fixedsize)
 {
        struct _attrlist_buf ab;
+       struct _attrlist_paths apaths = {.fullpathptr = NULL, .fullpathlenp = NULL,
+                                        .relpathptr = NULL, .relpathlenp = NULL,
+                                        .REALpathptr = NULL, .REALpathlenp = NULL};
        ssize_t buf_size;
        size_t copy_size;
-       ssize_t varsize;
+       ssize_t varsize;
        const char *vname = NULL;
        const char *cnp;
        ssize_t cnl;
        char *fullpathptr;
-       ssize_t fullpathlen;
+       ssize_t fullpathlen;
+       char *relpathptr;
+       ssize_t relpathlen;
+       char *REALpathptr;
+       ssize_t REALpathlen;
        int error;
        int proc_is64;
        int return_valid;
        int pack_invalid;
+       int is_realdev;
        int alloc_local_buf;
+       const int use_fork = options & FSOPT_ATTR_CMN_EXTENDED;
 
        proc_is64 = proc_is64bit(vfs_context_proc(ctx));
        ab.base = NULL;
@@ -2240,77 +2704,105 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
        cnl = 0;
        fullpathptr = NULL;
        fullpathlen = 0;
+       relpathptr = NULL;
+       relpathlen = 0;
+       REALpathptr = NULL;
+       REALpathlen = 0;
        error = 0;
        alloc_local_buf = 0;
 
        buf_size = (ssize_t)uio_resid(auio);
-       if ((buf_size <= 0) || (uio_iovcnt(auio) > 1))
-               return (EINVAL);
+       if ((buf_size <= 0) || (uio_iovcnt(auio) > 1)) {
+               return EINVAL;
+       }
 
        copy_size = 0;
        /* Check for special packing semantics */
        return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) ? 1 : 0;
        pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS) ? 1 : 0;
+       is_realdev = options & FSOPT_RETURN_REALDEV ? 1 : 0;
 
        if (pack_invalid) {
                /* Generate a valid mask for post processing */
-               bcopy(&(alp->commonattr), &ab.valid, sizeof (attribute_set_t));
+               bcopy(&(alp->commonattr), &ab.valid, sizeof(attribute_set_t));
        }
 
        /* did we ask for something the filesystem doesn't support? */
-       if (vap->va_active && !VATTR_ALL_SUPPORTED(vap)) {
-               vattr_get_alt_data(vp, alp, vap, return_valid, is_bulk,
+       if (vap->va_active &&
+           (!VATTR_ALL_SUPPORTED(vap)
+#if CONFIG_FIRMLINKS
+           /* For firmlink targets we have to overide what the FS returned for parentid */
+           ||
+           (!is_realdev && vp && (vp->v_flag & VFMLINKTARGET) && vp->v_fmlink &&
+           (alp->commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)))
+#endif
+           )) {
+               // this disables the selectors that were not supported by the filesystem
+               vattr_get_alt_data(vp, alp, vap, return_valid, is_bulk, is_realdev,
                    ctx);
 
                /* check again */
                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;
                        }
                }
 
-               if (error)
+               if (error) {
                        goto out;
+               }
        }
 
+       //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;
+               fullpathptr = (char*) zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO);
+               apaths.fullpathptr = fullpathptr;
+               apaths.fullpathlenp = &fullpathlen;
+       }
+
+       // only interpret fork attributes if they're used as new common attributes
+       if (vp && use_fork) {
+               if (alp->forkattr & (ATTR_CMNEXT_RELPATH)) {
+                       relpathptr = (char*) zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO);
+                       apaths.relpathptr = relpathptr;
+                       apaths.relpathlenp = &relpathlen;
+               }
+
+               if (alp->forkattr & (ATTR_CMNEXT_NOFIRMLINKPATH)) {
+                       REALpathptr = (char*) zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO);
+                       apaths.REALpathptr = REALpathptr;
+                       apaths.REALpathlenp = &REALpathlen;
                }
-               bzero(fullpathptr, MAXPATHLEN);
        }
 
        /*
         * Compute variable-space requirements.
         */
-       error = calc_varsize(vp, alp, vap, &varsize, fullpathptr, &fullpathlen,
-           &vname, &cnp, &cnl);
-       if (error)
+       error = calc_varsize(vp, alp, vap, &varsize, &apaths, &vname, &cnp, &cnl);
+       if (error) {
                goto out;
+       }
 
        /*
         * Allocate a target buffer for attribute results.
         *
         * Note that we won't ever copy out more than the caller requested, even though
         * we might have to allocate more than they offer so that the diagnostic checks
-        * don't result in a panic if the caller's buffer is too small..
+        * don't result in a panic if the caller's buffer is too small.
         */
        ab.allocated = fixedsize + varsize;
        /* Cast 'allocated' to an unsigned to verify allocation size */
-       if ( ((size_t)ab.allocated) > ATTR_MAX_BUFFER) {
+       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;
@@ -2328,8 +2820,9 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
 
                        newlen = (ab.allocated + 7) & ~0x07;
                        /* Align only if enough space for alignment */
-                       if (newlen <= (uint32_t)buf_size)
+                       if (newlen <= (uint32_t)buf_size) {
                                ab.allocated = newlen;
+                       }
                }
        }
 
@@ -2338,8 +2831,7 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
         * and big enough.
         */
        if (uio_isuserspace(auio) || (buf_size < ab.allocated)) {
-               MALLOC(ab.base, char *, ab.allocated, M_TEMP,
-                      M_ZERO | M_WAITOK);
+               ab.base = kheap_alloc(KHEAP_TEMP, ab.allocated, Z_ZERO | Z_WAITOK);
                alloc_local_buf = 1;
        } else {
                /*
@@ -2407,19 +2899,20 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
         */
        ab.fixedcursor = ab.base + sizeof(uint32_t);
        if (return_valid) {
-               ab.fixedcursor += sizeof (attribute_set_t);
-               bzero(&ab.actual, sizeof (ab.actual));
+               ab.fixedcursor += sizeof(attribute_set_t);
+               bzero(&ab.actual, sizeof(ab.actual));
        }
        ab.varcursor = ab.base + fixedsize;
        ab.needed = ab.allocated;
 
        /* common attributes ************************************************/
-       error = attr_pack_common(ctx, vp, alp, &ab, vap, proc_is64, cnp, cnl,
-           fullpathptr, fullpathlen, return_valid, pack_invalid, vtype, is_bulk); 
+       error = attr_pack_common(ctx, (options & FSOPT_RETURN_REALDEV ? mp : NULL),
+           vp, alp, &ab, vap, proc_is64, cnp, cnl, 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 **************************************************/
@@ -2428,15 +2921,24 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
                    pack_invalid, is_bulk);
        }
 
-       if (error)
+       /* common extended attributes *****************************************/
+       if (!error && use_fork) {
+               error = attr_pack_common_extended(mp, vp, alp, &ab, relpathptr, relpathlen,
+                   REALpathptr, REALpathlen, vap, return_valid, pack_invalid);
+       }
+
+       if (error) {
                goto out;
-       
+       }
+
        /* diagnostic */
-       if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize)
+       if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize) {
                panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
                    fixedsize, (long) (ab.fixedcursor - ab.base), alp->commonattr, alp->volattr);
-       if (!return_valid && ab.varcursor != (ab.base + ab.needed))
+       }
+       if (!return_valid && ab.varcursor != (ab.base + ab.needed)) {
                panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab.varcursor - ab.base), ab.needed);
+       }
 
        /*
         * In the compatible case, we report the smaller of the required and returned sizes.
@@ -2444,7 +2946,7 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, 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) ? (uint32_t)ab.needed : (uint32_t)lmin(ab.allocated, ab.needed);
 
        /* Return attribute set output if requested. */
        if (return_valid) {
@@ -2455,14 +2957,14 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
                        ab.actual.dirattr &= ab.valid.dirattr;
                        ab.actual.fileattr &= ab.valid.fileattr;
                }
-               bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual));
+               bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof(ab.actual));
        }
 
-       copy_size = imin(buf_size, ab.allocated);
+       copy_size = lmin(buf_size, ab.allocated);
 
        /* Only actually copyout as much out as the user buffer can hold */
        if (alloc_local_buf) {
-               error = uiomove(ab.base, copy_size, auio);
+               error = uiomove(ab.base, (int)copy_size, auio);
        } else {
                off_t orig_offset = uio_offset(auio);
 
@@ -2483,17 +2985,26 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
        }
 
 out:
-       if (vname)
+       if (vname) {
                vnode_putname(vname);
-       if (fullpathptr)
-               kfree(fullpathptr, MAXPATHLEN);
-       if (ab.base != NULL && alloc_local_buf)
-               FREE(ab.base, M_TEMP);
-       return (error);
+       }
+       if (fullpathptr) {
+               zfree(ZV_NAMEI, fullpathptr);
+       }
+       if (relpathptr) {
+               zfree(ZV_NAMEI, relpathptr);
+       }
+       if (REALpathptr) {
+               zfree(ZV_NAMEI, REALpathptr);
+       }
+       if (alloc_local_buf) {
+               kheap_free(KHEAP_TEMP, ab.base, ab.allocated);
+       }
+       return error;
 }
 
 errno_t
-vfs_attr_pack(vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options,
+vfs_attr_pack_ext(mount_t mp, vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options,
     struct vnode_attr *vap, __unused void *fndesc, vfs_context_t ctx)
 {
        int error;
@@ -2502,17 +3013,18 @@ vfs_attr_pack(vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options,
        struct attrlist orig_al;
        enum vtype v_type;
 
-       if (vp)
+       if (vp) {
                v_type = vnode_vtype(vp);
-       else
+       } else {
                v_type = vap->va_objtype;
+       }
 
        orig_al = *alp;
        orig_active = vap->va_active;
        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,
@@ -2520,15 +3032,22 @@ vfs_attr_pack(vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options,
                goto out;
        }
 
-       error = vfs_attr_pack_internal(vp, uio, alp, 
-           options|FSOPT_REPORT_FULLSIZE, vap, NULL, ctx, 1, v_type,
+       error = vfs_attr_pack_internal(mp, vp, uio, alp,
+           options | FSOPT_REPORT_FULLSIZE, vap, NULL, ctx, 1, v_type,
            fixedsize);
 
        VATTR_CLEAR_SUPPORTED_ALL(vap);
        vap->va_active = orig_active;
        *alp = orig_al;
 out:
-       return (error);
+       return error;
+}
+
+errno_t
+vfs_attr_pack(vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options,
+    struct vnode_attr *vap, __unused void *fndesc, vfs_context_t ctx)
+{
+       return vfs_attr_pack_ext(NULL, vp, uio, alp, options, vap, fndesc, ctx);
 }
 
 /*
@@ -2538,38 +3057,46 @@ out:
  * name obtained from some authoritative source (eg. readdir vnop); where
  * filesystems' getattr vnops do not support ATTR_CMN_NAME, the alt_name will be
  * used as the ATTR_CMN_NAME attribute returned in vnode_attr.va_name.
- * 
+ *
  */
 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;
-       ssize_t         fixedsize;
-       char            *va_name;
-       int             proc_is64;
-       int             error;
-       int             return_valid;
-       int             pack_invalid;
-       int             vtype = 0;
-       uio_t           auio;
-       char uio_buf[ UIO_SIZEOF(1)];
+       struct vnode_attr *va;
+       kauth_action_t  action;
+       ssize_t         fixedsize;
+       char            *va_name;
+       int             proc_is64;
+       int             error;
+       int             return_valid;
+       int             pack_invalid;
+       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;
+
+       if (bufferSize < sizeof(uint32_t)) {
+               return ERANGE;
+       }
 
        proc_is64 = proc_is64bit(vfs_context_proc(ctx));
 
        if (segflg == UIO_USERSPACE) {
-               if (proc_is64)
+               if (proc_is64) {
                        segflg = UIO_USERSPACE64;
-               else
+               } else {
                        segflg = UIO_USERSPACE32;
+               }
        }
        auio = uio_createwithbuffer(1, 0, segflg, UIO_READ,
-                   &uio_buf[0], sizeof(uio_buf));
+           &uio_buf[0], sizeof(uio_buf));
        uio_addiov(auio, attributeBuffer, bufferSize);
 
-       VATTR_INIT(&va);
+       va = kheap_alloc(KHEAP_TEMP, sizeof(struct vnode_attr), Z_WAITOK);
+       VATTR_INIT(va);
        va_name = NULL;
 
        if (alp->bitmapcount != ATTR_BIT_MAP_COUNT) {
@@ -2578,28 +3105,31 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
        }
 
        VFS_DEBUG(ctx, vp, "%p  ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
-           vp, p->p_comm, alp->commonattr, alp->volattr, alp->fileattr, alp->dirattr, alp->forkattr,
+           vp, vfs_context_proc(ctx)->p_comm, alp->commonattr, alp->volattr, alp->fileattr, alp->dirattr, alp->forkattr,
            (options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
 
 #if CONFIG_MACF
        error = mac_vnode_check_getattrlist(ctx, vp, alp);
-       if (error)
+       if (error) {
                goto out;
+       }
 #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 */
                error = getvolattrlist(ctx, vp, alp, attributeBuffer,
-                                      bufferSize, options, segflg, proc_is64);
+                   bufferSize, options, segflg, proc_is64);
                goto out;
        }
 
@@ -2614,17 +3144,29 @@ 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;
                }
                /* Keep invalid attrs from being uninitialized */
-               bzero(&va, sizeof (va));
+               bzero(va, sizeof(*va));
        }
 
        /* Pick up the vnode type.  If the FS is bad and changes vnode types on us, we
@@ -2635,7 +3177,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;
        }
@@ -2645,28 +3187,39 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
        }
 
 
-       if (va.va_active != 0) {
-               uint64_t va_active = va.va_active;
+       if (va->va_active != 0) {
+               uint64_t va_active = va->va_active;
 
                /*
                 * If we're going to ask for va_name, allocate a buffer to point it at
                 */
-               if (VATTR_IS_ACTIVE(&va, va_name)) {
-                       MALLOC_ZONE(va_name, char *, MAXPATHLEN, M_NAMEI,
-                           M_WAITOK);
-                       if (va_name == NULL) {
-                               error = ENOMEM;
-                               VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: cannot allocate va_name buffer");
-                               goto out;
+               if (VATTR_IS_ACTIVE(va, va_name)) {
+                       va_name = zalloc(ZV_NAMEI);
+                       /*
+                        * 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;
+
+               if (options & FSOPT_RETURN_REALDEV) {
+                       va->va_vaflags |= VA_REALFSID;
+               }
 
                /*
                 * Call the filesystem.
                 */
-               if ((error = vnode_getattr(vp, &va, ctx)) != 0) {
+               if ((error = vnode_getattr(vp, va, ctx)) != 0) {
                        VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
                        goto out;
                }
@@ -2681,7 +3234,7 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
                 * opportunity to change the values of attributes
                 * retrieved.
                 */
-               error = mac_vnode_check_getattr(ctx, file_cred, vp, &va);
+               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;
@@ -2690,32 +3243,38 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist  *alp,
                (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);
+               va->va_active = va_active;
+               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;
+               va->va_name = va_name;
        }
-       
-       error = vfs_attr_pack_internal(vp, auio, alp, options, &va, NULL, ctx,
+
+       error = vfs_attr_pack_internal(vp->v_mount, vp, auio, alp, options, va, NULL, ctx,
            0, vtype, fixedsize);
 
 out:
-       if (va_name)
-               FREE_ZONE(va_name, MAXPATHLEN, M_NAMEI);
-       if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL))
-               kauth_acl_free(va.va_acl);
+       if (va_name) {
+               zfree(ZV_NAMEI, va_name);
+       }
+       if (VATTR_IS_SUPPORTED(va, va_acl) && (va->va_acl != NULL)) {
+               kauth_acl_free(va->va_acl);
+       }
+       kheap_free(KHEAP_TEMP, va, sizeof(struct vnode_attr));
 
        VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
-       return(error);
+       return error;
 }
 
 int
@@ -2732,33 +3291,34 @@ fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval)
        fp = NULL;
        error = 0;
 
-       if ((error = file_vnode(uap->fd, &vp)) != 0)
-               return (error);
+       if ((error = fp_get_ftype(p, uap->fd, DTYPE_VNODE, EINVAL, &fp)) != 0) {
+               return error;
+       }
+       vp = (struct vnode *)fp->fp_glob->fg_data;
 
-       if ((error = fp_lookup(p, uap->fd, &fp, 0)) != 0 ||
-           (error = vnode_getwithref(vp)) != 0)
+       if ((error = vnode_getwithref(vp)) != 0) {
                goto out;
+       }
 
        /*
         * Fetch the attribute request.
         */
        error = copyin(uap->alist, &al, sizeof(al));
-       if (error)
-               goto out;
+       if (error) {
+               goto out_vnode_put;
+       }
 
        /* Default to using the vnode's name. */
        error = getattrlist_internal(ctx, vp, &al, uap->attributeBuffer,
-                                    uap->bufferSize, uap->options,
-                                    (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : \
-                                    UIO_USERSPACE32), NULL,
-                                    fp->f_fglob->fg_cred);
+           uap->bufferSize, uap->options,
+           (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : \
+           UIO_USERSPACE32), NULL,
+           fp->fp_glob->fg_cred);
 
+out_vnode_put:
+       vnode_put(vp);
 out:
-       if (fp)
-               fp_drop(p, uap->fd, fp, 0);
-       if (vp)
-               vnode_put(vp);
-       file_drop(uap->fd);
+       fp_drop(p, uap->fd, fp, 0);
 
        return error;
 }
@@ -2777,8 +3337,9 @@ getattrlistat_internal(vfs_context_t ctx, user_addr_t path,
        /*
         * Look up the file.
         */
-       if (!(options & FSOPT_NOFOLLOW))
+       if (!(options & FSOPT_NOFOLLOW)) {
                nameiflags |= FOLLOW;
+       }
 
        nameiflags |= AUDITVNPATH1;
        NDINIT(&nd, LOOKUP, OP_GETATTR, nameiflags, pathsegflg,
@@ -2786,18 +3347,19 @@ getattrlistat_internal(vfs_context_t ctx, user_addr_t path,
 
        error = nameiat(&nd, fd);
 
-       if (error)
-               return (error);
+       if (error) {
+               return error;
+       }
 
        vp = nd.ni_vp;
 
        error = getattrlist_internal(ctx, vp, alp, attributeBuffer,
            bufferSize, options, segflg, NULL, NOCRED);
-       
+
        /* Retain the namei reference until the getattrlist completes. */
        nameidone(&nd);
        vnode_put(vp);
-       return (error);
+       return error;
 }
 
 int
@@ -2813,13 +3375,14 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused int32_t *retval)
         * Fetch the attribute request.
         */
        error = copyin(uap->alist, &al, sizeof(al));
-       if (error)
+       if (error) {
                return error;
+       }
 
-       return (getattrlistat_internal(vfs_context_current(),
-           CAST_USER_ADDR_T(uap->path), &al,
-           CAST_USER_ADDR_T(uap->attributeBuffer), uap->bufferSize,
-           (uint64_t)uap->options, segflg, segflg, AT_FDCWD));
+       return getattrlistat_internal(vfs_context_current(),
+                  CAST_USER_ADDR_T(uap->path), &al,
+                  CAST_USER_ADDR_T(uap->attributeBuffer), uap->bufferSize,
+                  (uint64_t)uap->options, segflg, segflg, AT_FDCWD);
 }
 
 int
@@ -2835,13 +3398,14 @@ getattrlistat(proc_t p, struct getattrlistat_args *uap, __unused int32_t *retval
         * Fetch the attribute request.
         */
        error = copyin(uap->alist, &al, sizeof(al));
-       if (error)
+       if (error) {
                return error;
+       }
 
-       return (getattrlistat_internal(vfs_context_current(),
-           CAST_USER_ADDR_T(uap->path), &al,
-           CAST_USER_ADDR_T(uap->attributeBuffer), uap->bufferSize,
-           (uint64_t)uap->options, segflg, segflg, uap->fd));
+       return getattrlistat_internal(vfs_context_current(),
+                  CAST_USER_ADDR_T(uap->path), &al,
+                  CAST_USER_ADDR_T(uap->attributeBuffer), uap->bufferSize,
+                  (uint64_t)uap->options, segflg, segflg, uap->fd);
 }
 
 /*
@@ -2874,7 +3438,7 @@ refill_fd_direntries(vfs_context_t ctx, vnode_t dvp, struct fd_vn_data *fvd,
        if (fvd->fv_eofflag) {
                *eofflagp = 1;
                if (fvd->fv_buf) {
-                       FREE(fvd->fv_buf, M_FD_DIRBUF);
+                       kheap_free(KHEAP_DATA_BUFFERS, fvd->fv_buf, fvd->fv_bufallocsiz);
                        fvd->fv_buf = NULL;
                }
                return 0;
@@ -2906,7 +3470,8 @@ retry_alloc:
         * not copied out to user space.
         */
        if (!fvd->fv_buf) {
-               MALLOC(fvd->fv_buf, caddr_t, rdirbufsiz, M_FD_DIRBUF, M_WAITOK);
+               fvd->fv_buf = kheap_alloc(KHEAP_DATA_BUFFERS, rdirbufsiz, Z_WAITOK);
+               fvd->fv_bufallocsiz = rdirbufsiz;
                fvd->fv_bufdone = 0;
        }
 
@@ -2927,15 +3492,13 @@ retry_alloc:
                /* Save offsets */
                fvd->fv_soff = fvd->fv_eoff;
                fvd->fv_eoff = uio_offset(rdir_uio);
-                /* Save eofflag state but don't return EOF for this time.*/
+               /* Save eofflag state but don't return EOF for this time.*/
                fvd->fv_eofflag = eofflag;
                eofflag = 0;
-                /* Reset buffer parameters */
+               /* Reset buffer parameters */
                fvd->fv_bufsiz = rdirbufused;
                fvd->fv_bufdone = 0;
                bzero(fvd->fv_buf + rdirbufused, rdirbufsiz - rdirbufused);
-               /* Cache allocation size the Filesystem responded to */
-               fvd->fv_bufallocsiz = rdirbufsiz;
        } else if (!eofflag && (rdirbufsiz < FV_DIRBUF_MAX_SIZ)) {
                /*
                 * Some Filesystems have higher requirements for the
@@ -2947,8 +3510,7 @@ retry_alloc:
                 * from VNOP_READDIR is ignored until at least FV_DIRBUF_MAX_SIZ
                 * has been attempted.
                 */
-               FREE(fvd->fv_buf, M_FD_DIRBUF);
-               fvd->fv_buf = NULL;
+               kheap_free(KHEAP_DATA_BUFFERS, fvd->fv_buf, fvd->fv_bufallocsiz);
                rdirbufsiz = 2 * rdirbufsiz;
                fvd->fv_bufallocsiz = 0;
                goto retry_alloc;
@@ -2975,13 +3537,15 @@ retry_alloc:
         * time to free up directory entry buffer.
         */
        if ((error || eofflag) && fvd->fv_buf) {
-               FREE(fvd->fv_buf, M_FD_DIRBUF);
-               fvd->fv_buf = NULL;
+               kheap_free(KHEAP_DATA_BUFFERS, fvd->fv_buf, fvd->fv_bufallocsiz);
+               if (error) {
+                       fvd->fv_bufallocsiz = 0;
+               }
        }
 
        *eofflagp = eofflag;
 
-       return (error);
+       return error;
 }
 
 /*
@@ -3005,16 +3569,16 @@ get_direntry(vfs_context_t ctx, vnode_t dvp, struct fd_vn_data *fvd,
        if (!fvd->fv_bufsiz) {
                error = refill_fd_direntries(ctx, dvp, fvd, &eofflag);
                if (error) {
-                       return (error);
+                       return error;
                }
                if (eofflag) {
                        *eofflagp = eofflag;
-                       return (error);
+                       return error;
                }
        }
 
        *dpp = (struct direntry *)(fvd->fv_buf + fvd->fv_bufdone);
-       return (error);
+       return error;
 }
 
 /*
@@ -3078,7 +3642,7 @@ get_error_attributes(vnode_t vp, struct attrlist *alp, uint64_t options,
 {
        size_t fsiz, vsiz;
        struct _attrlist_buf ab;
-       int namelen;
+       size_t namelen;
        kauth_action_t action;
        struct attrlist al;
        int needs_error_attr = (alp->commonattr & ATTR_CMN_ERROR);
@@ -3114,7 +3678,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;
@@ -3125,7 +3689,7 @@ get_error_attributes(vnode_t vp, struct attrlist *alp, uint64_t options,
        ab.needed = fsiz + vsiz;
 
        /* Fill in the size needed */
-       *((uint32_t *)ab.base) = ab.needed;
+       *((uint32_t *)ab.base) = (uint32_t)ab.needed;
        if (ab.needed > (ssize_t)kern_attr_buf_siz) {
                goto out;
        }
@@ -3149,8 +3713,8 @@ get_error_attributes(vnode_t vp, struct attrlist *alp, uint64_t options,
         * Initialise the value for ATTR_CMN_RETURNED_ATTRS and leave space
         * Leave space for filling in its value here at the end.
         */
-       bzero(&ab.actual, sizeof (ab.actual));
-       ab.fixedcursor += sizeof (attribute_set_t);
+       bzero(&ab.actual, sizeof(ab.actual));
+       ab.fixedcursor += sizeof(attribute_set_t);
 
        ab.allocated = ab.needed;
 
@@ -3174,7 +3738,7 @@ get_error_attributes(vnode_t vp, struct attrlist *alp, uint64_t options,
         * ATTR_CMN_NAME.
         */
        ab.actual.commonattr |= ATTR_CMN_NAME | ATTR_CMN_RETURNED_ATTRS;
-       bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual));
+       bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof(ab.actual));
 out:
        return;
 }
@@ -3205,7 +3769,7 @@ readdirattr(vnode_t dvp, struct fd_vn_data *fvd, uio_t auio,
        *eofflagp = 0;
 
        if (uio_iovcnt(auio) > 1) {
-               return (EINVAL);
+               return EINVAL;
        }
 
        /*
@@ -3217,10 +3781,10 @@ readdirattr(vnode_t dvp, struct fd_vn_data *fvd, uio_t auio,
                kern_attr_buf_siz = ATTR_MAX_BUFFER;
        } else if (kern_attr_buf_siz == 0) {
                /* Nothing to do */
-               return (error);
+               return error;
        }
 
-       MALLOC(kern_attr_buf, caddr_t, kern_attr_buf_siz, M_TEMP, M_WAITOK);
+       kern_attr_buf = kheap_alloc(KHEAP_TEMP, kern_attr_buf_siz, Z_WAITOK);
 
        while (uio_resid(auio) > (user_ssize_t)MIN_BUF_SIZE_REQUIRED) {
                struct direntry *dp;
@@ -3259,8 +3823,7 @@ readdirattr(vnode_t dvp, struct fd_vn_data *fvd, uio_t auio,
                 */
                if (dp->d_name[dp->d_namlen] != '\0') {
                        if (!max_path_name_buf) {
-                               MALLOC(max_path_name_buf, caddr_t, MAXPATHLEN,
-                                   M_TEMP, M_WAITOK);
+                               max_path_name_buf = zalloc_flags(ZV_NAMEI, Z_WAITOK);
                        }
                        bcopy(dp->d_name, max_path_name_buf, dp->d_namlen);
                        max_path_name_buf[dp->d_namlen] = '\0';
@@ -3271,17 +3834,17 @@ readdirattr(vnode_t dvp, struct fd_vn_data *fvd, uio_t auio,
 
                /*
                 * We have an iocount on the directory already.
-                * 
+                *
                 * Note that we supply NOCROSSMOUNT to the namei call as we attempt to acquire
                 * a vnode for this particular entry.  This is because the native call will
                 * (likely) attempt to emit attributes based on its own metadata in order to avoid
                 * creating vnodes where posssible.  If the native call is not going to  walk
                 * up the vnode mounted-on chain in order to find the top-most mount point, then we
-                * should not either in this emulated readdir+getattrlist() approach.  We  
+                * should not either in this emulated readdir+getattrlist() approach.  We
                 * will be responsible for setting DIR_MNTSTATUS_MNTPOINT on that directory that
-                * contains a mount point.  
+                * contains a mount point.
                 */
-               NDINIT(&nd, LOOKUP, OP_GETATTR, (AUDITVNPATH1 | USEDVP | NOCROSSMOUNT), 
+               NDINIT(&nd, LOOKUP, OP_GETATTR, (AUDITVNPATH1 | USEDVP | NOCROSSMOUNT),
                    UIO_SYSSPACE, CAST_USER_ADDR_T(name_buffer), ctx);
 
                nd.ni_dvp = dvp;
@@ -3304,7 +3867,7 @@ 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, 
+                   options | FSOPT_REPORT_FULLSIZE, UIO_SYSSPACE,
                    CAST_DOWN_EXPLICIT(char *, name_buffer),
                    NOCRED);
 
@@ -3373,7 +3936,7 @@ readdirattr(vnode_t dvp, struct fd_vn_data *fvd, uio_t auio,
                        entlen += pad_bytes;
                }
                *((uint32_t *)kern_attr_buf) = (uint32_t)entlen;
-               error = uiomove(kern_attr_buf, min(entlen, kern_attr_buf_siz),
+               error = uiomove(kern_attr_buf, min((int)entlen, (int)kern_attr_buf_siz),
                    auio);
 
                if (error) {
@@ -3393,13 +3956,13 @@ readdirattr(vnode_t dvp, struct fd_vn_data *fvd, uio_t auio,
        }
 
        if (max_path_name_buf) {
-               FREE(max_path_name_buf, M_TEMP);
+               zfree(ZV_NAMEI, max_path_name_buf);
        }
 
        /*
         * At this point, kern_attr_buf is always allocated
         */
-       FREE(kern_attr_buf, M_TEMP);
+       kheap_free(KHEAP_TEMP, kern_attr_buf, kern_attr_buf_siz);
 
        /*
         * Always set the offset to the last succesful offset
@@ -3407,11 +3970,16 @@ readdirattr(vnode_t dvp, struct fd_vn_data *fvd, uio_t auio,
         */
        uio_setoffset(auio, fvd->fv_eoff);
 
-       return (error);
+       return error;
 }
 
+/* common attributes that only require KAUTH_VNODE_LIST_DIRECTORY */
+#define LIST_DIR_ATTRS    (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE |  \
+                          ATTR_CMN_FILEID | ATTR_CMN_RETURNED_ATTRS |  \
+                          ATTR_CMN_ERROR)
+
 /*
- *int getattrlistbulk(int dirfd, struct attrlist *alist, void *attributeBuffer,
+ * int getattrlistbulk(int dirfd, struct attrlist *alist, void *attributeBuffer,
  *    size_t bufferSize, uint64_t options)
  *
  * Gets directory entries alongwith their attributes in the same way
@@ -3423,14 +3991,15 @@ int
 getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
 {
        struct attrlist al;
-       vnode_t dvp;
+       vnode_t dvp = NULLVP;
        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;
-       char uio_buf[ UIO_SIZEOF(1) ];
+       char uio_buf[UIO_SIZEOF(1)];
        kauth_action_t action;
        int eofflag;
        uint64_t options;
@@ -3439,20 +4008,23 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
        *retval = 0;
 
        error = fp_getfvp(p, uap->dirfd, &fp, &dvp);
-       if (error)
-               return (error);
+       if (error) {
+               return error;
+       }
 
        count = 0;
        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) {
+       if ((fp->fp_glob->fg_flag & FREAD) == 0) {
                /*
-               AUDIT_ARG(vnpath_withref, dvp, ARG_VNODE1);
-               */
+                *  AUDIT_ARG(vnpath_withref, dvp, ARG_VNODE1);
+                */
                error = EBADF;
+               dvp = NULLVP;
                goto out;
        }
 
@@ -3464,11 +4036,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;
@@ -3476,8 +4043,9 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
 
                /* switch directory to snapshot directory */
                error = vnode_get_snapdir(dvp, &snapdvp, ctx);
-               if (error)
+               if (error) {
                        goto out;
+               }
                vnode_put(dvp);
                dvp = snapdvp;
        }
@@ -3489,13 +4057,14 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
 
 #if CONFIG_MACF
        error = mac_file_check_change_offset(vfs_context_ucred(ctx),
-                                            fp->f_fglob);
-       if (error)
+           fp->fp_glob);
+       if (error) {
                goto out;
+       }
 #endif
        /*
         * XXX : Audit Support
-        *AUDIT_ARG(vnpath, dvp, ARG_VNODE1);
+        * AUDIT_ARG(vnpath, dvp, ARG_VNODE1);
         */
 
        options = uap->options | FSOPT_ATTR_CMN_EXTENDED;
@@ -3519,20 +4088,21 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
 #endif /* MAC */
 
        /*
-        * If the only item requested is file names, we can let that past with
-        * just LIST_DIRECTORY.  If they want any other attributes, that means
-        * they need SEARCH as well.
+        * Requested attributes that are available in the direntry struct, with the addition
+        * of ATTR_CMN_RETURNED_ATTRS and ATTR_CMN_ERROR, can be let past with just LIST_DIRECTORY.
+        * Any other requested attributes require SEARCH as well.
         */
        action = KAUTH_VNODE_LIST_DIRECTORY;
-       if ((al.commonattr & ~ATTR_CMN_NAME) || al.fileattr || al.dirattr)
+       if ((al.commonattr & ~LIST_DIR_ATTRS) || al.fileattr || al.dirattr) {
                action |= KAUTH_VNODE_SEARCH;
-       
+       }
+
        error = vnode_authorize(dvp, NULL, action, ctx);
        if (error) {
                goto out;
        }
 
-       fvdata = (struct fd_vn_data *)fp->f_fglob->fg_vn_data;
+       fvdata = (struct fd_vn_data *)fp->fp_glob->fg_vn_data;
        if (!fvdata) {
                panic("Directory expected to have fg_vn_data");
        }
@@ -3545,11 +4115,10 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
         * traversal needs to be restarted (Any existing state in the
         * directory buffer is removed as well).
         */
-       if (!fp->f_fglob->fg_offset) {
+       if (!fp->fp_glob->fg_offset) {
                fvdata->fv_offset = 0;
-               if (fvdata->fv_buf)
-                       FREE(fvdata->fv_buf, M_FD_DIRBUF);
-               fvdata->fv_buf = NULL;
+               kheap_free(KHEAP_DATA_BUFFERS, fvdata->fv_buf,
+                   fvdata->fv_bufallocsiz);
                fvdata->fv_bufsiz = 0;
                fvdata->fv_bufdone = 0;
                fvdata->fv_soff = 0;
@@ -3573,8 +4142,8 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
            (ATTR_CMN_UUID | ATTR_CMN_GRPUUID | ATTR_CMN_EXTENDED_SECURITY)) ||
            !(al.commonattr & ATTR_CMN_OBJTYPE)) {
                error = ENOTSUP;
-        } else {
-               struct vnode_attr va;
+       } else {
+               struct vnode_attr *va;
                char *va_name;
 
                if (fvdata->fv_eofflag && !fvdata->fv_buf) {
@@ -3589,18 +4158,26 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
                        eofflag = 0;
                        count = 0;
 
-                       VATTR_INIT(&va);
-                       MALLOC(va_name, char *, MAXPATHLEN, M_TEMP,
-                           M_WAITOK | M_ZERO);
-                       va.va_name = va_name;
+                       va = kheap_alloc(KHEAP_TEMP, sizeof(struct vnode_attr), Z_WAITOK);
 
-                       (void)getattrlist_setupvattr_all(&al, &va, VNON, NULL,
-                           IS_64BIT_PROCESS(p));
+                       VATTR_INIT(va);
+                       va_name = zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO);
+                       va->va_name = va_name;
 
-                       error = VNOP_GETATTRLISTBULK(dvp, &al, &va, auio, NULL,
+                       (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);
+                       zfree(ZV_NAMEI, va_name);
+                       kheap_free(KHEAP_TEMP, va, sizeof(struct vnode_attr));
 
                        /*
                         * cache state of eofflag.
@@ -3619,13 +4196,15 @@ 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) {
                fvdata->fv_offset = uio_offset(auio);
-               fp->f_fglob->fg_offset = fvdata->fv_offset;
+               fp->fp_glob->fg_offset = fvdata->fv_offset;
                *retval = count;
                error = 0;
        } else if (!error && !eofflag) {
@@ -3644,36 +4223,37 @@ out:
 
        file_drop(uap->dirfd);
 
-       return (error);
+       return error;
 }
 
 static int
 attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
 {
        /* make sure we have enough source data */
-       if ((*cursor) + size > end)
-               return(EINVAL);
+       if ((*cursor) + size > end) {
+               return EINVAL;
+       }
 
        bcopy(*cursor, buf, size);
        *cursor += size;
-       return(0);
+       return 0;
 }
 
-#define ATTR_UNPACK(v)         do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
-#define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = _f;} while(0)
-#define ATTR_UNPACK_TIME(v, is64)                              \
-       do {                                                    \
-               if (is64) {                                     \
-                       struct user64_timespec us;              \
-                       ATTR_UNPACK(us);                        \
-                       v.tv_sec = us.tv_sec;                   \
-                       v.tv_nsec = us.tv_nsec;                 \
-               } else {                                        \
-                       struct user32_timespec us;              \
-                       ATTR_UNPACK(us);                        \
-                       v.tv_sec = us.tv_sec;                   \
-                       v.tv_nsec = us.tv_nsec;                 \
-               }                                               \
+#define ATTR_UNPACK(v)          do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
+#define ATTR_UNPACK_CAST(t, v)  do { t _f; ATTR_UNPACK(_f); v = (typeof(v))_f;} while(0)
+#define ATTR_UNPACK_TIME(v, is64)                               \
+       do {                                                    \
+               if (is64) {                                     \
+                       struct user64_timespec us;              \
+                       ATTR_UNPACK(us);                        \
+                       v.tv_sec = (unsigned long)us.tv_sec;                   \
+                       v.tv_nsec = (long)us.tv_nsec;                 \
+               } else {                                        \
+                       struct user32_timespec us;              \
+                       ATTR_UNPACK(us);                        \
+                       v.tv_sec = us.tv_sec;                   \
+                       v.tv_nsec = us.tv_nsec;                 \
+               }                                               \
        } while(0)
 
 
@@ -3686,10 +4266,10 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
        struct attrlist al;
        struct vnode_attr va;
        struct attrreference ar;
-       kauth_action_t  action;
-       char            *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
-       int             proc_is64, error;
-       uint32_t        nace;
+       kauth_action_t  action;
+       char            *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
+       int             proc_is64, error;
+       uint32_t        nace;
        kauth_filesec_t rfsec;
 
        user_buf = NULL;
@@ -3698,17 +4278,31 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
        error = 0;
        proc_is64 = proc_is64bit(p);
        VATTR_INIT(&va);
-       
+
        /*
         * Fetch the attribute set and validate.
         */
-       if ((error = copyin(uap->alist, (caddr_t) &al, sizeof (al))))
+       if ((error = copyin(uap->alist, (caddr_t) &al, sizeof(al)))) {
                goto out;
+       }
        if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
                error = EINVAL;
                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);
@@ -3735,19 +4329,17 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
 
        /*
         * If the caller's bitmaps indicate that there are no attributes to set,
-        * then exit early.  In particular, we want to avoid the MALLOC below
-        * since the caller's bufferSize could be zero, and MALLOC of zero bytes
-        * returns a NULL pointer, which would cause setattrlist to return ENOMEM.
+        * then exit early.
         */
        if (al.commonattr == 0 &&
-               (al.volattr & ~ATTR_VOL_INFO) == 0 &&
-               al.dirattr == 0 &&
-               al.fileattr == 0 &&
-               al.forkattr == 0) {
+           (al.volattr & ~ATTR_VOL_INFO) == 0 &&
+           al.dirattr == 0 &&
+           al.fileattr == 0 &&
+           al.forkattr == 0) {
                error = 0;
                goto out;
        }
-               
+
        /*
         * Make the naive assumption that the caller has supplied a reasonable buffer
         * size.  We could be more careful by pulling in the fixed-size region, checking
@@ -3762,7 +4354,7 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
                error = ENOMEM;
                goto out;
        }
-       MALLOC(user_buf, char *, uap->bufferSize, M_TEMP, M_WAITOK);
+       user_buf = kheap_alloc(KHEAP_DATA_BUFFERS, uap->bufferSize, Z_WAITOK);
        if (user_buf == NULL) {
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap->bufferSize);
                error = ENOMEM;
@@ -3776,8 +4368,9 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
 
 #if CONFIG_MACF
        error = mac_vnode_check_setattrlist(ctx, vp, &al);
-       if (error)
+       if (error) {
                goto out;
+       }
 #endif /* MAC */
 
        /*
@@ -3837,12 +4430,12 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
                ATTR_UNPACK(va.va_flags);
                VATTR_SET_ACTIVE(&va, va_flags);
 #if CONFIG_MACF
-               if ((error = mac_vnode_check_setflags(ctx, vp, va.va_flags)) != 0)
+               if ((error = mac_vnode_check_setflags(ctx, vp, va.va_flags)) != 0) {
                        goto out;
+               }
 #endif
        }
        if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
-
                /*
                 * We are (for now) passed a kauth_filesec_t, but all we want from
                 * it is the ACL.
@@ -3857,18 +4450,19 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
 
                cp += ar.attr_dataoffset;
                rfsec = (kauth_filesec_t)cp;
-               if (((((char *)rfsec) + KAUTH_FILESEC_SIZE(0)) > bufend) ||                     /* no space for acl */
+               if (((((char *)rfsec) + KAUTH_FILESEC_SIZE(0)) > bufend) ||                     /* no space for acl */
                    (rfsec->fsec_magic != KAUTH_FILESEC_MAGIC) ||       /* bad magic */
                    (KAUTH_FILESEC_COPYSIZE(rfsec) != ar.attr_length) || /* size does not match */
-                   ((cp + KAUTH_FILESEC_COPYSIZE(rfsec)) > bufend)) {  /* ACEs overrun buffer */
+                   ((cp + KAUTH_FILESEC_COPYSIZE(rfsec)) > bufend)) {  /* ACEs overrun buffer */
                        error = EINVAL;
                        VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied", ar.attr_length);
                        goto out;
                }
                nace = rfsec->fsec_entrycount;
-               if (nace == KAUTH_FILESEC_NOACL)
+               if (nace == KAUTH_FILESEC_NOACL) {
                        nace = 0;
-               if (nace > KAUTH_ACL_MAX_ENTRIES) {                     /* ACL size invalid */
+               }
+               if (nace > KAUTH_ACL_MAX_ENTRIES) {                     /* ACL size invalid */
                        error = EINVAL;
                        VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied");
                        goto out;
@@ -3878,8 +4472,7 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
                        /* deleting ACL */
                        VATTR_SET(&va, va_acl, NULL);
                } else {
-                       
-                       if (nace > KAUTH_ACL_MAX_ENTRIES) {                     /* ACL size invalid */
+                       if (nace > KAUTH_ACL_MAX_ENTRIES) {                     /* ACL size invalid */
                                error = EINVAL;
                                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: supplied ACL is too large");
                                goto out;
@@ -3895,16 +4488,25 @@ 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) {
                if (al.volattr & ATTR_VOL_NAME) {
                        volname = cursor;
-                       ATTR_UNPACK(ar);        
+                       ATTR_UNPACK(ar);
                        /* attr_length cannot be 0! */
                        if ((ar.attr_dataoffset < 0) || (ar.attr_length == 0) ||
-                               (ar.attr_length > uap->bufferSize) ||
-                               (uap->bufferSize - ar.attr_length < (unsigned)ar.attr_dataoffset)) {
+                           (ar.attr_length > uap->bufferSize) ||
+                           (uap->bufferSize - ar.attr_length < (unsigned)ar.attr_dataoffset)) {
                                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied (2) ", ar.attr_dataoffset);
                                error = EINVAL;
                                goto out;
@@ -3985,8 +4587,9 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
 
 #if CONFIG_MACF
        mac_vnode_notify_setattrlist(ctx, vp, &al);
-       if (VATTR_IS_ACTIVE(&va, va_flags))
+       if (VATTR_IS_ACTIVE(&va, va_flags)) {
                mac_vnode_notify_setflags(ctx, vp, va.va_flags);
+       }
 #endif
 
        /*
@@ -3997,8 +4600,9 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
                        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)
+                               if (error != 0) {
                                        goto out;
+                               }
                        } else {
                                /* XXX should never get here */
                        }
@@ -4007,29 +4611,29 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
                }
        }
 
-       /* 
+       /*
         * Set the volume name, if we have one
         */
-       if (volname != NULL)
-       {
+       if (volname != NULL) {
                struct vfs_attr vs;
-               
+
                VFSATTR_INIT(&vs);
-               
-               vs.f_vol_name = volname;        /* References the setattrlist buffer directly */
+
+               vs.f_vol_name = volname;        /* References the setattrlist buffer directly */
                VFSATTR_WANTED(&vs, f_vol_name);
-               
+
 #if CONFIG_MACF
                error = mac_mount_check_setattr(ctx, vp->v_mount, &vs);
-               if (error != 0)
+               if (error != 0) {
                        goto out;
+               }
 #endif
 
                if ((error = vfs_setattr(vp->v_mount, &vs, ctx)) != 0) {
                        VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setting volume name failed");
                        goto out;
                }
-               
+
                if (!VFSATTR_ALL_SUPPORTED(&vs)) {
                        error = EINVAL;
                        VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not set volume name");
@@ -4038,12 +4642,11 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con
        }
 
        /* all done and successful */
-       
+
 out:
-       if (user_buf != NULL)
-               FREE(user_buf, M_TEMP);
+       kheap_free(KHEAP_DATA_BUFFERS, user_buf, uap->bufferSize);
        VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d", error);
-       return(error);
+       return error;
 }
 
 int
@@ -4051,8 +4654,8 @@ setattrlist(proc_t p, struct setattrlist_args *uap, __unused int32_t *retval)
 {
        struct vfs_context *ctx;
        struct nameidata nd;
-       vnode_t         vp = NULL;
-       u_long          nameiflags;
+       vnode_t         vp = NULL;
+       uint32_t          nameiflags;
        int error = 0;
 
        ctx = vfs_context_current();
@@ -4061,18 +4664,62 @@ setattrlist(proc_t p, struct setattrlist_args *uap, __unused int32_t *retval)
         * Look up the file.
         */
        nameiflags = AUDITVNPATH1;
-       if ((uap->options & FSOPT_NOFOLLOW) == 0)
+       if ((uap->options & FSOPT_NOFOLLOW) == 0) {
                nameiflags |= FOLLOW;
+       }
        NDINIT(&nd, LOOKUP, OP_SETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
-       if ((error = namei(&nd)) != 0)
+       if ((error = namei(&nd)) != 0) {
                goto out;
+       }
        vp = nd.ni_vp;
        nameidone(&nd);
 
        error = setattrlist_internal(vp, uap, p, ctx);
 out:
-       if (vp != NULL)
+       if (vp != NULL) {
+               vnode_put(vp);
+       }
+       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;
 }
 
@@ -4080,18 +4727,19 @@ int
 fsetattrlist(proc_t p, struct fsetattrlist_args *uap, __unused int32_t *retval)
 {
        struct vfs_context *ctx;
-       vnode_t         vp = NULL;
-       int             error;
+       vnode_t         vp = NULL;
+       int             error;
        struct setattrlist_args ap;
 
        ctx = vfs_context_current();
 
-       if ((error = file_vnode(uap->fd, &vp)) != 0)
-               return (error);
+       if ((error = file_vnode(uap->fd, &vp)) != 0) {
+               return error;
+       }
 
        if ((error = vnode_getwithref(vp)) != 0) {
                file_drop(uap->fd);
-               return(error);
+               return error;
        }
 
        ap.path = 0;
@@ -4102,9 +4750,9 @@ fsetattrlist(proc_t p, struct fsetattrlist_args *uap, __unused int32_t *retval)
 
        error = setattrlist_internal(vp, &ap, p, ctx);
        file_drop(uap->fd);
-       if (vp != NULL)
+       if (vp != NULL) {
                vnode_put(vp);
+       }
 
        return error;
 }
-