]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/vfs/vfs_attrlist.c
xnu-2422.90.20.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_attrlist.c
index dcc9e1b7165fa6c61822d254d12ca03d35f590a6..25c57ee40336dc17da56f7acbe0ecccc82386d05 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 1995-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 1995-2012 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -66,64 +66,131 @@ struct _attrlist_buf {
        char    *varcursor;
        ssize_t allocated;
        ssize_t needed;
        char    *varcursor;
        ssize_t allocated;
        ssize_t needed;
+       attribute_set_t actual;
+       attribute_set_t valid;
 };
 
 
 /*
 };
 
 
 /*
- * Pack (count) bytes from (source) into (buf).
+ * Attempt to pack a fixed width attribute of size (count) bytes from
+ * source to our attrlist buffer.
  */
 static void
 attrlist_pack_fixed(struct _attrlist_buf *ab, void *source, ssize_t count)
 {
  */
 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;
 
-       /* how much room left in the buffer? */
-       fit = imin(count, ab->allocated - (ab->fixedcursor - ab->base));
-       if (fit > 0)
+       /*
+        * 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 
+        * 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. 
+        */ 
+       fit = lmin(count, ab->allocated - (ab->fixedcursor - ab->base));
+       if (fit > 0) {
+               /* Copy in as much as we can */
                bcopy(source, ab->fixedcursor, fit);
                bcopy(source, ab->fixedcursor, fit);
+       }
 
 
-       /* always move in increments of 4 */
+       /* always move in increments of 4, even if we didn't pack an attribute. */
        ab->fixedcursor += roundup(count, 4);
 }
        ab->fixedcursor += roundup(count, 4);
 }
+
+/*
+ * 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 
+ * attribute that tells the caller where to look, and the actual variable width data.
+ */
 static void
 static void
-attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t count, const void *ext, ssize_t extcount)
-{
-       struct attrreference    ar;
+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;
 
        ssize_t fit;
 
-       /* pack the reference 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;
        attrlist_pack_fixed(ab, &ar, sizeof(ar));
 
        ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
        ar.attr_length = count + extcount;
        attrlist_pack_fixed(ab, &ar, sizeof(ar));
 
-       /* calculate space and pack the variable object */
-       fit = imin(count, ab->allocated - (ab->varcursor - ab->base));
+       /* 
+        * 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. 
+        */
+       fit = lmin(count, ab->allocated - (ab->varcursor - ab->base));
        if (fit > 0) {
        if (fit > 0) {
-               if (source != NULL)
+               if (source != NULL) {
                        bcopy(source, ab->varcursor, fit);
                        bcopy(source, ab->varcursor, fit);
+               }
                ab->varcursor += fit;
        }
                ab->varcursor += fit;
        }
-       fit = imin(extcount, ab->allocated - (ab->varcursor - ab->base));
+
+       /* Compute the available space for the second attribute */
+       fit = lmin(extcount, ab->allocated - (ab->varcursor - ab->base));
        if (fit > 0) {
        if (fit > 0) {
-               if (ext != NULL)
+               /* Copy in data for the second attribute (if needed) if there is room */
+               if (ext != NULL) {
                        bcopy(ext, ab->varcursor, fit);
                        bcopy(ext, ab->varcursor, fit);
+               }
                ab->varcursor += fit;
        }
        /* always move in increments of 4 */
        ab->varcursor = (char *)roundup((uintptr_t)ab->varcursor, 4);
 }
                ab->varcursor += fit;
        }
        /* always move in increments of 4 */
        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.
+ */
 static void
 attrlist_pack_variable(struct _attrlist_buf *ab, const void *source, ssize_t count)
 {
        attrlist_pack_variable2(ab, source, count, NULL, 0);
 }
 static void
 attrlist_pack_variable(struct _attrlist_buf *ab, const void *source, ssize_t count)
 {
        attrlist_pack_variable2(ab, source, count, NULL, 0);
 }
+
+/*
+ * Attempt to pack a string. This is a special case of a variable width attribute.
+ *
+ * If "source" is NULL, then an empty string ("") will be packed.  If "source" is
+ * not NULL, but "count" is zero, then "source" is assumed to be a NUL-terminated
+ * C-string.  If "source" is not NULL and "count" is not zero, then only the first
+ * "count" bytes of "source" will be copied, and a NUL terminator will be added.
+ *
+ * If the attrlist buffer doesn't have enough room to hold the entire string (including
+ * NUL terminator), then copy as much as will fit.  The attrlist buffer's "varcursor"
+ * will always be updated based on the entire length of the string (including NUL
+ * terminator); this means "varcursor" may end up pointing beyond the end of the
+ * allocated buffer space.
+ */
 static void
 attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count)
 {
 static void
 attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count)
 {
-       struct attrreference    ar;
+       struct attrreference ar;
        ssize_t fit, space;
 
        ssize_t fit, space;
 
-       
        /*
         * Supplied count is character count of string text, excluding trailing nul
         * which we always supply here.
        /*
         * Supplied count is character count of string text, excluding trailing nul
         * which we always supply here.
@@ -135,25 +202,49 @@ attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count
        }
 
        /*
        }
 
        /*
-        * Make the reference and pack it.
-        * Note that this is entirely independent of how much we get into
-        * the buffer.
+        * Construct the fixed-width attribute that refers to this string. 
         */
        ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
        ar.attr_length = count + 1;
        attrlist_pack_fixed(ab, &ar, sizeof(ar));
         */
        ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
        ar.attr_length = count + 1;
        attrlist_pack_fixed(ab, &ar, sizeof(ar));
-       
-       /* calculate how much of the string text we can copy, and do that */
+
+       /*
+        * Now compute how much available memory we have to copy the string text.
+        *
+        * space = the number of bytes available in the attribute buffer to hold the
+        *         string's value.
+        *
+        * fit = the number of bytes to copy from the start of the string into the
+        *       attribute buffer, NOT including the NUL terminator.  If the attribute
+        *       buffer is large enough, this will be the string's length; otherwise, it
+        *       will be equal to "space".
+        */
        space = ab->allocated - (ab->varcursor - ab->base);
        space = ab->allocated - (ab->varcursor - ab->base);
-       fit = imin(count, space);
-       if (fit > 0)
+       fit = lmin(count, space);
+       if (space > 0) {
+               /* 
+                * 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
+                * terminator (space < fit + 1), then the string value in the attribute
+                * buffer will NOT be NUL terminated!
+                *
+                * NOTE 2: bcopy() will do nothing if the length ("fit") is zero.
+                * Therefore, we don't bother checking for that here.
+                */
                bcopy(source, ab->varcursor, fit);
                bcopy(source, ab->varcursor, fit);
-       /* is there room for our trailing nul? */
-       if (space > fit)
-               ab->varcursor[fit] = '\0';
+               /* is there room for our trailing nul? */
+               if (space > fit) {
+                       ab->varcursor[fit++] = '\0';
+                       /* 'fit' now the number of bytes AFTER adding in the NUL */
+               }
+       }
+       /* 
+        * always move in increments of 4 (including the trailing NUL)
+        */
+       ab->varcursor += roundup((count+1), 4);
 
 
-       /* always move in increments of 4 */
-       ab->varcursor += roundup(count + 1, 4);
 }
 
 #define ATTR_PACK4(AB, V)                                                 \
 }
 
 #define ATTR_PACK4(AB, V)                                                 \
@@ -182,10 +273,11 @@ attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count
 #define ATTR_PACK_TIME(b, v, is64)                                                     \
        do {                                                                            \
                if (is64) {                                                             \
 #define ATTR_PACK_TIME(b, v, is64)                                                     \
        do {                                                                            \
                if (is64) {                                                             \
-                       struct user_timespec us = {v.tv_sec, v.tv_nsec};                \
+                       struct user64_timespec us = {v.tv_sec, v.tv_nsec};              \
                        ATTR_PACK(&b, us);                                              \
                } else {                                                                \
                        ATTR_PACK(&b, us);                                              \
                } else {                                                                \
-                       ATTR_PACK8(b, v);                                               \
+                       struct user32_timespec us = {v.tv_sec, v.tv_nsec};              \
+                       ATTR_PACK(&b, us);                                              \
                }                                                                       \
        } while(0)
 
                }                                                                       \
        } while(0)
 
@@ -220,8 +312,17 @@ static struct getvolattrlist_attrtab getvolattrlist_common_tab[] = {
        {ATTR_CMN_ACCESSMASK,   0,                              sizeof(uint32_t)},
        {ATTR_CMN_FLAGS,        0,                              sizeof(uint32_t)},
        {ATTR_CMN_USERACCESS,   0,                              sizeof(uint32_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)},
        {0, 0, 0}
 };
        {0, 0, 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)},
 
 static struct getvolattrlist_attrtab getvolattrlist_vol_tab[] = {
        {ATTR_VOL_FSTYPE,               0,                                              sizeof(uint32_t)},
@@ -242,6 +343,7 @@ static struct getvolattrlist_attrtab getvolattrlist_vol_tab[] = {
        {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_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_VOL_ATTRIBUTES,           VFSATTR_BIT(f_attributes),                      sizeof(vol_attributes_attr_t)},
        {ATTR_VOL_INFO, 0, 0},
        {0, 0, 0}
@@ -261,9 +363,9 @@ getvolattrlist_parsetab(struct getvolattrlist_attrtab *tab, attrgroup_t attrs, s
                        vsp->f_active |= tab->bits;
                        if (tab->size == ATTR_TIME_SIZE) {
                                if (is_64bit) {
                        vsp->f_active |= tab->bits;
                        if (tab->size == ATTR_TIME_SIZE) {
                                if (is_64bit) {
-                                       *sizep += sizeof(struct user_timespec);
+                                       *sizep += sizeof(struct user64_timespec);
                                } else {
                                } else {
-                                       *sizep += sizeof(struct timespec);
+                                       *sizep += sizeof(struct user32_timespec);
                                }
                        } else {
                                *sizep += tab->size;
                                }
                        } else {
                                *sizep += tab->size;
@@ -290,9 +392,17 @@ getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t
         * Parse the above tables.
         */
        *sizep = sizeof(uint32_t);      /* length count */
         * Parse the above tables.
         */
        *sizep = sizeof(uint32_t);      /* length count */
-       if (alp->commonattr &&
-           (error = getvolattrlist_parsetab(getvolattrlist_common_tab, alp->commonattr, vsp, sizep, is_64bit)) != 0)
-               return(error);
+       if (alp->commonattr) {
+               if ((alp->commonattr & ATTR_CMN_VOL_INVALID) &&
+                   (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) == 0) {
+                       return (EINVAL);
+               }
+               if ((error = getvolattrlist_parsetab(getvolattrlist_common_tab,
+                                                   alp->commonattr, vsp, sizep,
+                                                   is_64bit)) != 0) {
+                       return(error);
+               }
+       }
        if (alp->volattr &&
            (error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit)) != 0)
                return(error);
        if (alp->volattr &&
            (error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit)) != 0)
                return(error);
@@ -300,6 +410,38 @@ getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t
        return(0);
 }
 
        return(0);
 }
 
+/*
+ * Given the attributes listed in asp and those supported
+ * in the vsp, fixup the asp attributes to reflect any
+ * missing attributes from the file system
+ */
+static void
+getvolattrlist_fixupattrs(attribute_set_t *asp, struct vfs_attr *vsp)
+{
+       struct getvolattrlist_attrtab *tab;
+
+       if (asp->commonattr) {
+               tab = getvolattrlist_common_tab;
+               do {
+                       if ((tab->attr & asp->commonattr) &&
+                           (tab->bits != 0) &&
+                           ((tab->bits & vsp->f_supported) == 0)) {
+                               asp->commonattr &= ~tab->attr;
+                       }
+               } while ((++tab)->attr != 0);
+       }
+       if (asp->volattr) {
+               tab = getvolattrlist_vol_tab;
+               do {
+                       if ((tab->attr & asp->volattr) &&
+                           (tab->bits != 0) &&
+                           ((tab->bits & vsp->f_supported) == 0)) {
+                               asp->volattr &= ~tab->attr;
+                       }
+               } while ((++tab)->attr != 0);
+       }
+}
+
 /*
  * Table-driven setup for all valid common/dir/file/fork attributes against files.
  */
 /*
  * Table-driven setup for all valid common/dir/file/fork attributes against files.
  */
@@ -310,6 +452,11 @@ struct getattrlist_attrtab {
        ssize_t         size;
        kauth_action_t  action;
 };
        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 
+ * 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},
 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},
@@ -336,13 +483,15 @@ static struct getattrlist_attrtab getattrlist_common_tab[] = {
        {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_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},
        {0, 0, 0, 0}
 };
        {0, 0, 0, 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},
 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_ENTRYCOUNT falls back to va_nlink-2 if va_nchildren isn't supported, so request va_nlink just in case */
-       {ATTR_DIR_ENTRYCOUNT,   VATTR_BIT(va_nlink),            0,                              KAUTH_VNODE_READ_ATTRIBUTES},
        {ATTR_DIR_MOUNTSTATUS,  0,                              sizeof(uint32_t),               KAUTH_VNODE_READ_ATTRIBUTES},
        {0, 0, 0, 0}
 };
        {ATTR_DIR_MOUNTSTATUS,  0,                              sizeof(uint32_t),               KAUTH_VNODE_READ_ATTRIBUTES},
        {0, 0, 0, 0}
 };
@@ -370,18 +519,18 @@ static struct getattrlist_attrtab getattrlist_file_tab[] = {
                                 ATTR_VOL_ALLOCATIONCLUMP |  ATTR_VOL_IOBLOCKSIZE |  \
                                 ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS |  \
                                 ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES |  \
                                 ATTR_VOL_ALLOCATIONCLUMP |  ATTR_VOL_IOBLOCKSIZE |  \
                                 ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS |  \
                                 ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES |  \
-                                ATTR_VOL_ATTRIBUTES)
+                                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 |  \
 
 #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_ACCTIME | ATTR_CMN_FNDRINFO |  \
+                                ATTR_CMN_FNDRINFO |  \
                                 ATTR_CMN_OWNERID  | ATTR_CMN_GRPID |  \
                                 ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS |  \
                                 ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
                                 ATTR_CMN_OWNERID  | ATTR_CMN_GRPID |  \
                                 ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS |  \
                                 ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
-                                ATTR_CMN_PARENTID)
+                                ATTR_CMN_PARENTID | ATTR_CMN_RETURNED_ATTRS)
 
 #define VFS_DFLT_ATTR_DIR      (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
 
 
 #define VFS_DFLT_ATTR_DIR      (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
 
@@ -405,14 +554,16 @@ getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs, struct
                        vap->va_active |= tab->bits;
                        if (tab->size == ATTR_TIME_SIZE) {
                                if (is_64bit) {
                        vap->va_active |= tab->bits;
                        if (tab->size == ATTR_TIME_SIZE) {
                                if (is_64bit) {
-                                       *sizep += sizeof(struct user_timespec);
+                                       *sizep += sizeof(struct user64_timespec);
                                } else {
                                } else {
-                                       *sizep += sizeof(struct timespec);
+                                       *sizep += sizeof(struct user32_timespec);
                                }
                        } else {
                                *sizep += tab->size;
                        }
                        *actionp |= tab->action;
                                }
                        } else {
                                *sizep += tab->size;
                        }
                        *actionp |= tab->action;
+                       if (attrs == recognised)
+                               break;  /* all done, get out */
                }
        } while ((++tab)->attr != 0);
        
                }
        } while ((++tab)->attr != 0);
        
@@ -449,6 +600,68 @@ getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *si
        return(0);
 }
 
        return(0);
 }
 
+/*
+ * Given the attributes listed in asp and those supported
+ * in the vap, fixup the asp attributes to reflect any
+ * missing attributes from the file system
+ */
+static void
+getattrlist_fixupattrs(attribute_set_t *asp, struct vnode_attr *vap)
+{
+       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 
+                        * 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 
+                        * 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 
+                        * 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
+                        * on.  This is done so that we can uncheck those bits and re-request
+                        * a vnode_getattr from the filesystem again.
+                        */
+                       if ((tab->attr & asp->commonattr) &&
+                           (tab->bits & vap->va_active) &&
+                           (tab->bits & vap->va_supported) == 0) {
+                               asp->commonattr &= ~tab->attr;
+                       }
+               } while ((++tab)->attr != 0);
+       }
+       if (asp->dirattr) {
+               tab = getattrlist_dir_tab;
+               do {
+                       if ((tab->attr & asp->dirattr) &&
+                           (tab->bits & vap->va_active) &&
+                           (vap->va_supported & tab->bits) == 0) {
+                               asp->dirattr &= ~tab->attr;
+                       }
+               } while ((++tab)->attr != 0);
+       }
+       if (asp->fileattr) {
+               tab = getattrlist_file_tab;
+               do {
+                       if ((tab->attr & asp->fileattr) &&
+                           (tab->bits & vap->va_active) &&
+                           (vap->va_supported & tab->bits) == 0) {
+                               asp->fileattr &= ~tab->attr;
+                       }
+               } while ((++tab)->attr != 0);
+       }
+}
+
 static int
 setattrlist_setfinderinfo(vnode_t vp, char *fndrinfo, struct vfs_context *ctx)
 {
 static int
 setattrlist_setfinderinfo(vnode_t vp, char *fndrinfo, struct vfs_context *ctx)
 {
@@ -512,7 +725,8 @@ getattrlist_findnamecomp(const char *mn, const char **np, ssize_t *nl)
 
 
 static int
 
 
 static int
-getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,  vfs_context_t ctx, int is_64bit)
+getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
+               vfs_context_t ctx, int is_64bit)
 {
        struct vfs_attr vs;
        struct vnode_attr va;
 {
        struct vfs_attr vs;
        struct vnode_attr va;
@@ -521,7 +735,10 @@ getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
        ssize_t         fixedsize, varsize;
        const char      *cnp = NULL;    /* protected by ATTR_CMN_NAME */
        ssize_t         cnl = 0;        /* protected by ATTR_CMN_NAME */
        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;
        mount_t         mnt;
+       int             return_valid;
+       int             pack_invalid;
 
        ab.base = NULL;
        VATTR_INIT(&va);
 
        ab.base = NULL;
        VATTR_INIT(&va);
@@ -529,7 +746,21 @@ getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
        vs.f_vol_name = NULL;
        mnt = vp->v_mount;
 
        vs.f_vol_name = NULL;
        mnt = vp->v_mount;
 
-       
+       /* Check for special packing semantics */
+       return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS);
+       pack_invalid = (uap->options & FSOPT_PACK_INVAL_ATTRS);
+       if (pack_invalid) {
+               /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
+               if (!return_valid) {
+                       error = EINVAL;
+                       goto out;
+               }
+               /* Keep invalid attrs from being uninitialized */
+               bzero(&vs, sizeof (vs));
+               /* Generate a valid mask for post processing */
+               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
        /*
         * 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
@@ -540,7 +771,7 @@ getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
                goto out;
        }
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
                goto out;
        }
-       
+
        /*
         * Set up the vfs_attr structure and call the filesystem.
         */
        /*
         * Set up the vfs_attr structure and call the filesystem.
         */
@@ -636,11 +867,21 @@ getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
 
                        /* check to see if our fixups were enough */
                        if (!VFSATTR_ALL_SUPPORTED(&vs)) {
 
                        /* check to see if our fixups were enough */
                        if (!VFSATTR_ALL_SUPPORTED(&vs)) {
-                               error = EINVAL;
-                               VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not get all requested volume attributes");
-                               VFS_DEBUG(ctx, vp, "ATTRLIST -        wanted %016llx got %016llx missing %016llx",
-                                   vs.f_active, vs.f_supported, vs.f_active & ~vs.f_supported);
-                               goto out;
+                               if (return_valid) {
+                                       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 {
+                                               /* Adjust the requested attributes */
+                                               getvolattrlist_fixupattrs((attribute_set_t *)&alp->commonattr, &vs);
+                                       }
+                               } else {
+                                       error = EINVAL;
+                                       goto out;
+                               }
                        }
                }
        }
                        }
                }
        }
@@ -660,8 +901,15 @@ getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
                        goto out;
                }
 
                        goto out;
                }
 
-               if (VATTR_IS_ACTIVE(&va, va_encoding) && !VATTR_IS_SUPPORTED(&va, va_encoding))
-                       VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
+               if (VATTR_IS_ACTIVE(&va, va_encoding) &&
+                   !VATTR_IS_SUPPORTED(&va, va_encoding)) {
+                       if (!return_valid || pack_invalid)
+                               /* use kTextEncodingMacUnicode */
+                               VATTR_RETURN(&va, va_encoding, 0x7e);
+                       else
+                               /* don't use a default */
+                               alp->commonattr &= ~ATTR_CMN_SCRIPT;
+               }
        }
 
        /*
        }
 
        /*
@@ -682,6 +930,9 @@ getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
                                /* just use "/" as name */
                                cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
                        }
                                /* just use "/" as name */
                                cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
                        }
+                       else {
+                               release_str = 1;
+                       }
                        cnl = strlen(cnp);
                }
                else {
                        cnl = strlen(cnp);
                }
                else {
@@ -704,7 +955,7 @@ getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
         * Note that since we won't ever copy out more than the caller requested,
         * we never need to allocate more than they offer.
         */
         * 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 = imin(uap->bufferSize, fixedsize + varsize);
+       ab.allocated = ulmin(uap->bufferSize, fixedsize + varsize);
        if (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);
        if (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);
@@ -721,67 +972,112 @@ getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
         * Pack results into the destination buffer.
         */
        ab.fixedcursor = ab.base + sizeof(uint32_t);
         * Pack results into the destination buffer.
         */
        ab.fixedcursor = ab.base + sizeof(uint32_t);
+       if (return_valid) {
+               ab.fixedcursor += sizeof (attribute_set_t);
+               bzero(&ab.actual, sizeof (ab.actual));
+       }
        ab.varcursor = ab.base + fixedsize;
        ab.needed = fixedsize + varsize;
 
        /* common attributes **************************************************/
        ab.varcursor = ab.base + fixedsize;
        ab.needed = fixedsize + varsize;
 
        /* common attributes **************************************************/
-       if (alp->commonattr & ATTR_CMN_NAME)
+       if (alp->commonattr & ATTR_CMN_NAME) {
                attrlist_pack_string(&ab, cnp, cnl);
                attrlist_pack_string(&ab, cnp, cnl);
-       if (alp->commonattr & ATTR_CMN_DEVID)
+               ab.actual.commonattr |= ATTR_CMN_NAME;
+       }
+       if (alp->commonattr & ATTR_CMN_DEVID) {
                ATTR_PACK4(ab, mnt->mnt_vfsstat.f_fsid.val[0]);
                ATTR_PACK4(ab, mnt->mnt_vfsstat.f_fsid.val[0]);
-       if (alp->commonattr & ATTR_CMN_FSID)
+               ab.actual.commonattr |= ATTR_CMN_DEVID;
+       }
+       if (alp->commonattr & ATTR_CMN_FSID) {
                ATTR_PACK8(ab, mnt->mnt_vfsstat.f_fsid);
                ATTR_PACK8(ab, mnt->mnt_vfsstat.f_fsid);
-       if (alp->commonattr & ATTR_CMN_OBJTYPE)
-               ATTR_PACK4(ab, 0);
-       if (alp->commonattr & ATTR_CMN_OBJTAG)
+               ab.actual.commonattr |= ATTR_CMN_FSID;
+       }
+       if (alp->commonattr & ATTR_CMN_OBJTYPE) {
+               if (!return_valid || pack_invalid)
+                       ATTR_PACK4(ab, 0);
+       }
+       if (alp->commonattr & ATTR_CMN_OBJTAG) {
                ATTR_PACK4(ab, vp->v_tag);
                ATTR_PACK4(ab, vp->v_tag);
+               ab.actual.commonattr |= ATTR_CMN_OBJTAG;
+       }
        if (alp->commonattr & ATTR_CMN_OBJID) {
        if (alp->commonattr & ATTR_CMN_OBJID) {
-               fsobj_id_t f = {0, 0};
-               ATTR_PACK8(ab, f);
+               if (!return_valid || pack_invalid) {
+                       fsobj_id_t f = {0, 0};
+                       ATTR_PACK8(ab, f);
+               }
        }
        if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
        }
        if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
-               fsobj_id_t f = {0, 0};
-               ATTR_PACK8(ab, f);
+               if (!return_valid || pack_invalid) {
+                       fsobj_id_t f = {0, 0};
+                       ATTR_PACK8(ab, f);
+               }
        }
        if (alp->commonattr & ATTR_CMN_PAROBJID) {
        }
        if (alp->commonattr & ATTR_CMN_PAROBJID) {
-               fsobj_id_t f = {0, 0};
-               ATTR_PACK8(ab, f);
+               if (!return_valid || pack_invalid) {
+                       fsobj_id_t f = {0, 0};
+                       ATTR_PACK8(ab, f);
+               }
        }
        /* note that this returns the encoding for the volume name, not the node name */
        }
        /* note that this returns the encoding for the volume name, not the node name */
-       if (alp->commonattr & ATTR_CMN_SCRIPT)
+       if (alp->commonattr & ATTR_CMN_SCRIPT) {
                ATTR_PACK4(ab, va.va_encoding);
                ATTR_PACK4(ab, va.va_encoding);
-       if (alp->commonattr & ATTR_CMN_CRTIME)
+               ab.actual.commonattr |= ATTR_CMN_SCRIPT;
+       }
+       if (alp->commonattr & ATTR_CMN_CRTIME) {
                ATTR_PACK_TIME(ab, vs.f_create_time, is_64bit);
                ATTR_PACK_TIME(ab, vs.f_create_time, is_64bit);
-       if (alp->commonattr & ATTR_CMN_MODTIME)
-               ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
-       if (alp->commonattr & ATTR_CMN_CHGTIME)
+               ab.actual.commonattr |= ATTR_CMN_CRTIME;
+       }
+       if (alp->commonattr & ATTR_CMN_MODTIME) {
                ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
                ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
-       if (alp->commonattr & ATTR_CMN_ACCTIME)
+               ab.actual.commonattr |= ATTR_CMN_MODTIME;
+       }
+       if (alp->commonattr & ATTR_CMN_CHGTIME) {
+               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);
                ATTR_PACK_TIME(ab, vs.f_access_time, is_64bit);
-       if (alp->commonattr & ATTR_CMN_BKUPTIME)
+               ab.actual.commonattr |= ATTR_CMN_ACCTIME;
+       }
+       if (alp->commonattr & ATTR_CMN_BKUPTIME) {
                ATTR_PACK_TIME(ab, vs.f_backup_time, is_64bit);
                ATTR_PACK_TIME(ab, vs.f_backup_time, is_64bit);
+               ab.actual.commonattr |= ATTR_CMN_BKUPTIME;
+       }
        if (alp->commonattr & ATTR_CMN_FNDRINFO) {
                char f[32];
                /*
                 * This attribute isn't really Finder Info, at least for HFS.
                 */
                if (vp->v_tag == VT_HFS) {
        if (alp->commonattr & ATTR_CMN_FNDRINFO) {
                char f[32];
                /*
                 * This attribute isn't really Finder Info, at least for HFS.
                 */
                if (vp->v_tag == VT_HFS) {
-                       if ((error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, (caddr_t)&f, 0, ctx)) != 0)
+                       error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, (caddr_t)&f, 0, ctx);
+                       if (error == 0) {
+                               attrlist_pack_fixed(&ab, f, sizeof(f));
+                               ab.actual.commonattr |= ATTR_CMN_FNDRINFO;
+                       } else if (!return_valid) {
                                goto out;
                                goto out;
-               } else {
+                       }
+               } else if (!return_valid || pack_invalid) {
                        /* XXX we could at least pass out the volume UUID here */
                        bzero(&f, sizeof(f));
                        /* XXX we could at least pass out the volume UUID here */
                        bzero(&f, sizeof(f));
+                       attrlist_pack_fixed(&ab, f, sizeof(f));
                }
                }
-               attrlist_pack_fixed(&ab, f, sizeof(f));
        }
        }
-       if (alp->commonattr & ATTR_CMN_OWNERID)
+       if (alp->commonattr & ATTR_CMN_OWNERID) {
                ATTR_PACK4(ab, va.va_uid);
                ATTR_PACK4(ab, va.va_uid);
-       if (alp->commonattr & ATTR_CMN_GRPID)
+               ab.actual.commonattr |= ATTR_CMN_OWNERID;
+       }
+       if (alp->commonattr & ATTR_CMN_GRPID) {
                ATTR_PACK4(ab, va.va_gid);
                ATTR_PACK4(ab, va.va_gid);
-       if (alp->commonattr & ATTR_CMN_ACCESSMASK)
+               ab.actual.commonattr |= ATTR_CMN_GRPID;
+       }
+       if (alp->commonattr & ATTR_CMN_ACCESSMASK) {
                ATTR_PACK_CAST(&ab, uint32_t, va.va_mode);
                ATTR_PACK_CAST(&ab, uint32_t, va.va_mode);
-       if (alp->commonattr & ATTR_CMN_FLAGS)
+               ab.actual.commonattr |= ATTR_CMN_ACCESSMASK;
+       }
+       if (alp->commonattr & ATTR_CMN_FLAGS) {
                ATTR_PACK4(ab, va.va_flags);
                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 (vnode_isdir(vp)) {
        if (alp->commonattr & ATTR_CMN_USERACCESS) {    /* XXX this is expensive and also duplicate work */
                uint32_t        perms = 0;
                if (vnode_isdir(vp)) {
@@ -818,44 +1114,97 @@ getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
 #endif /* MAC */
                KAUTH_DEBUG("ATTRLIST - returning user access %x", perms);
                ATTR_PACK4(ab, perms);
 #endif /* MAC */
                KAUTH_DEBUG("ATTRLIST - returning user access %x", perms);
                ATTR_PACK4(ab, perms);
+               ab.actual.commonattr |= ATTR_CMN_USERACCESS;
+       }
+       /*
+        * The following common volume attributes are only
+        * packed when the pack_invalid mode is enabled.
+        */
+       if (pack_invalid) {
+               uint64_t fid = 0;
+
+               if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY)
+                       attrlist_pack_variable(&ab, NULL, 0);
+               if (alp->commonattr & ATTR_CMN_UUID)
+                       ATTR_PACK(&ab, kauth_null_guid);
+               if (alp->commonattr & ATTR_CMN_GRPUUID)
+                       ATTR_PACK(&ab, kauth_null_guid);
+               if (alp->commonattr & ATTR_CMN_FILEID)
+                       ATTR_PACK8(ab, fid);
+               if (alp->commonattr & ATTR_CMN_PARENTID)
+                       ATTR_PACK8(ab, fid);
        }
 
        /* volume attributes **************************************************/
 
        }
 
        /* volume attributes **************************************************/
 
-       if (alp->volattr & ATTR_VOL_FSTYPE)
+       if (alp->volattr & ATTR_VOL_FSTYPE) {
                ATTR_PACK_CAST(&ab, uint32_t, vfs_typenum(mnt));
                ATTR_PACK_CAST(&ab, uint32_t, vfs_typenum(mnt));
-       if (alp->volattr & ATTR_VOL_SIGNATURE)
+               ab.actual.volattr |= ATTR_VOL_FSTYPE;
+       }
+       if (alp->volattr & ATTR_VOL_SIGNATURE) {
                ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature);
                ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature);
-       if (alp->volattr & ATTR_VOL_SIZE)
+               ab.actual.volattr |= ATTR_VOL_SIGNATURE;
+       }
+       if (alp->volattr & ATTR_VOL_SIZE) {
                ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_blocks);
                ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_blocks);
-       if (alp->volattr & ATTR_VOL_SPACEFREE)
+               ab.actual.volattr |= ATTR_VOL_SIZE;
+       }
+       if (alp->volattr & ATTR_VOL_SPACEFREE) {
                ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bfree);
                ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bfree);
-       if (alp->volattr & ATTR_VOL_SPACEAVAIL)
+               ab.actual.volattr |= ATTR_VOL_SPACEFREE;
+       }
+       if (alp->volattr & ATTR_VOL_SPACEAVAIL) {
                ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bavail);
                ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bavail);
-       if (alp->volattr & ATTR_VOL_MINALLOCATION)
+               ab.actual.volattr |= ATTR_VOL_SPACEAVAIL;
+       }
+       if (alp->volattr & ATTR_VOL_MINALLOCATION) {
                ATTR_PACK_CAST(&ab, off_t, vs.f_bsize);
                ATTR_PACK_CAST(&ab, off_t, vs.f_bsize);
-       if (alp->volattr & ATTR_VOL_ALLOCATIONCLUMP)
+               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 */
-       if (alp->volattr & ATTR_VOL_IOBLOCKSIZE)
+               ab.actual.volattr |= ATTR_VOL_ALLOCATIONCLUMP;
+       }
+       if (alp->volattr & ATTR_VOL_IOBLOCKSIZE) {
                ATTR_PACK_CAST(&ab, uint32_t, vs.f_iosize);
                ATTR_PACK_CAST(&ab, uint32_t, vs.f_iosize);
-       if (alp->volattr & ATTR_VOL_OBJCOUNT)
+               ab.actual.volattr |= ATTR_VOL_IOBLOCKSIZE;
+       }
+       if (alp->volattr & ATTR_VOL_OBJCOUNT) {
                ATTR_PACK_CAST(&ab, uint32_t, vs.f_objcount);
                ATTR_PACK_CAST(&ab, uint32_t, vs.f_objcount);
-       if (alp->volattr & ATTR_VOL_FILECOUNT)
+               ab.actual.volattr |= ATTR_VOL_OBJCOUNT;
+       }
+       if (alp->volattr & ATTR_VOL_FILECOUNT) {
                ATTR_PACK_CAST(&ab, uint32_t, vs.f_filecount);
                ATTR_PACK_CAST(&ab, uint32_t, vs.f_filecount);
-       if (alp->volattr & ATTR_VOL_DIRCOUNT)
+               ab.actual.volattr |= ATTR_VOL_FILECOUNT;
+       }
+       if (alp->volattr & ATTR_VOL_DIRCOUNT) {
                ATTR_PACK_CAST(&ab, uint32_t, vs.f_dircount);
                ATTR_PACK_CAST(&ab, uint32_t, vs.f_dircount);
-       if (alp->volattr & ATTR_VOL_MAXOBJCOUNT)
+               ab.actual.volattr |= ATTR_VOL_DIRCOUNT;
+       }
+       if (alp->volattr & ATTR_VOL_MAXOBJCOUNT) {
                ATTR_PACK_CAST(&ab, uint32_t, vs.f_maxobjcount);
                ATTR_PACK_CAST(&ab, uint32_t, vs.f_maxobjcount);
-       if (alp->volattr & ATTR_VOL_MOUNTPOINT) 
+               ab.actual.volattr |= ATTR_VOL_MAXOBJCOUNT;
+       }
+       if (alp->volattr & ATTR_VOL_MOUNTPOINT) {
                attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntonname, 0);
                attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntonname, 0);
-       if (alp->volattr & ATTR_VOL_NAME)
+               ab.actual.volattr |= ATTR_VOL_MOUNTPOINT;
+       }
+       if (alp->volattr & ATTR_VOL_NAME) {
                attrlist_pack_string(&ab, vs.f_vol_name, 0);
                attrlist_pack_string(&ab, vs.f_vol_name, 0);
-       if (alp->volattr & ATTR_VOL_MOUNTFLAGS)
+               ab.actual.volattr |= ATTR_VOL_NAME;
+       }
+       if (alp->volattr & ATTR_VOL_MOUNTFLAGS) {
                ATTR_PACK_CAST(&ab, uint32_t, mnt->mnt_flag);
                ATTR_PACK_CAST(&ab, uint32_t, mnt->mnt_flag);
-       if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE)
+               ab.actual.volattr |= ATTR_VOL_MOUNTFLAGS;
+       }
+       if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE) {
                attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntfromname, 0);
                attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntfromname, 0);
-       if (alp->volattr & ATTR_VOL_ENCODINGSUSED)
-               ATTR_PACK_CAST(&ab, uint64_t, ~0LL);    /* return all encodings */
+               ab.actual.volattr |= ATTR_VOL_MOUNTEDDEVICE;
+       }
+       if (alp->volattr & ATTR_VOL_ENCODINGSUSED) {
+               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 */
                if (vfs_extendedsecurity(mnt)) {
        if (alp->volattr & ATTR_VOL_CAPABILITIES) {
                /* fix up volume capabilities */
                if (vfs_extendedsecurity(mnt)) {
@@ -865,6 +1214,11 @@ getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
                }
                vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
                ATTR_PACK(&ab, vs.f_capabilities);
                }
                vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
                ATTR_PACK(&ab, vs.f_capabilities);
+               ab.actual.volattr |= ATTR_VOL_CAPABILITIES;
+       }
+       if (alp->volattr & ATTR_VOL_UUID) {
+               ATTR_PACK(&ab, vs.f_uuid);
+               ab.actual.volattr |= ATTR_VOL_UUID;
        }
        if (alp->volattr & ATTR_VOL_ATTRIBUTES) {
                /* fix up volume attribute information */
        }
        if (alp->volattr & ATTR_VOL_ATTRIBUTES) {
                /* fix up volume attribute information */
@@ -881,14 +1235,15 @@ getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
                        vs.f_attributes.nativeattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
                }
                ATTR_PACK(&ab, vs.f_attributes);
                        vs.f_attributes.nativeattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
                }
                ATTR_PACK(&ab, vs.f_attributes);
+               ab.actual.volattr |= ATTR_VOL_ATTRIBUTES;
        }
        
        /* diagnostic */
        }
        
        /* diagnostic */
-       if ((ab.fixedcursor - ab.base) != fixedsize)
-               panic("packed field size mismatch; allocated %ld but packed %d for common %08x vol %08x",
-                   fixedsize, ab.fixedcursor - ab.base, alp->commonattr, alp->volattr);
-       if (ab.varcursor != (ab.base + ab.needed))
-               panic("packed variable field size mismatch; used %d but expected %ld", ab.varcursor - ab.base, ab.needed);
+       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))
+               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.
 
        /*
         * In the compatible case, we report the smaller of the required and returned sizes.
@@ -898,11 +1253,24 @@ getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
         */
        *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
        
         */
        *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
        
+       /* Return attribute set output if requested. */
+       if (return_valid) {
+               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));
+       }
        error = copyout(ab.base, uap->attributeBuffer, ab.allocated);
 
 out:
        if (vs.f_vol_name != NULL)
                kfree(vs.f_vol_name, MAXPATHLEN);
        error = copyout(ab.base, uap->attributeBuffer, ab.allocated);
 
 out:
        if (vs.f_vol_name != NULL)
                kfree(vs.f_vol_name, MAXPATHLEN);
+       if (release_str) {
+               vnode_putname(cnp);
+       }
        if (ab.base != NULL)
                FREE(ab.base, M_TEMP);
        VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
        if (ab.base != NULL)
                FREE(ab.base, M_TEMP);
        VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
@@ -912,46 +1280,36 @@ out:
 /*
  * Obtain attribute information about a filesystem object.
  */
 /*
  * Obtain attribute information about a filesystem object.
  */
-int
-getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
+
+static int
+getattrlist_internal(vnode_t vp, struct getattrlist_args *uap, 
+               __unused struct componentname *getattr_name, proc_t p, vfs_context_t ctx)
 {
        struct attrlist al;
        struct vnode_attr va;
 {
        struct attrlist al;
        struct vnode_attr va;
-       struct vfs_context *ctx;
-       struct nameidata nd;
        struct _attrlist_buf ab;
        struct _attrlist_buf ab;
-       vnode_t         vp;
-       u_long          nameiflags;
        kauth_action_t  action;
        ssize_t         fixedsize, varsize;
        const char      *cnp;
        const char      *vname = NULL;
        kauth_action_t  action;
        ssize_t         fixedsize, varsize;
        const char      *cnp;
        const char      *vname = NULL;
+       char    *fullpathptr;
+       ssize_t         fullpathlen;
        ssize_t         cnl;
        int             proc_is64;
        int             error;
        ssize_t         cnl;
        int             proc_is64;
        int             error;
+       int             return_valid;
+       int             pack_invalid;
+       int             vtype = 0;
+       uint32_t        perms = 0;
 
 
-       ctx = vfs_context_current();
-       vp = NULL;
-       error = 0;
        proc_is64 = proc_is64bit(p);
        VATTR_INIT(&va);
        va.va_name = NULL;
        ab.base = NULL;
        cnp = "unknown";
        cnl = 0;
        proc_is64 = proc_is64bit(p);
        VATTR_INIT(&va);
        va.va_name = NULL;
        ab.base = NULL;
        cnp = "unknown";
        cnl = 0;
-
-       /*
-        * Look up the file.
-        */
-       nameiflags = NOTRIGGER | AUDITVNPATH1;
-       if (!(uap->options & FSOPT_NOFOLLOW))
-               nameiflags |= FOLLOW;
-       NDINIT(&nd, LOOKUP, nameiflags, UIO_USERSPACE, uap->path, ctx);
-
-       if ((error = namei(&nd)) != 0)
-               goto out;
-       vp = nd.ni_vp;
-       nameidone(&nd);
+       fullpathptr = NULL;
+       fullpathlen = 0;
 
        /*
         * Fetch the attribute request.
 
        /*
         * Fetch the attribute request.
@@ -966,7 +1324,7 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
        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, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
            (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
        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, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
            (uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
-       
+
 #if CONFIG_MACF
        error = mac_vnode_check_getattrlist(ctx, vp, &al);
        if (error)
 #if CONFIG_MACF
        error = mac_vnode_check_getattrlist(ctx, vp, &al);
        if (error)
@@ -988,10 +1346,31 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
                goto out;
        }
 
                goto out;
        }
 
+       /* Check for special packing semantics */
+       return_valid = (al.commonattr & ATTR_CMN_RETURNED_ATTRS);
+       pack_invalid = (uap->options & FSOPT_PACK_INVAL_ATTRS);
+       if (pack_invalid) {
+               /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
+               if (!return_valid || al.forkattr) {
+                       error = EINVAL;
+                       goto out;
+               }
+               /* Keep invalid attrs from being uninitialized */
+               bzero(&va, sizeof (va));
+               /* Generate a valid mask for post processing */
+               bcopy(&al.commonattr, &ab.valid, sizeof (attribute_set_t));
+       }
+
+       /* Pick up the vnode type.  If the FS is bad and changes vnode types on us, we
+        * will have a valid snapshot that we can work from here.
+        */
+       vtype = vp->v_type;
+
+
        /*
         * Set up the vnode_attr structure and authorise.
         */
        /*
         * Set up the vnode_attr structure and authorise.
         */
-       if ((error = getattrlist_setupvattr(&al, &va, &fixedsize, &action, proc_is64, vnode_isdir(vp))) != 0) {
+       if ((error = getattrlist_setupvattr(&al, &va, &fixedsize, &action, proc_is64, (vtype == VDIR))) != 0) {
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
                goto out;
        }
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
                goto out;
        }
@@ -1000,6 +1379,19 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
                goto out;
        }
 
                goto out;
        }
 
+       /*
+        * If we're asking for the full path, allocate a buffer for that.
+        */
+       if (al.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;
+               }
+       }
+
+
        if (va.va_active != 0) {
                /*
                 * If we're going to ask for va_name, allocate a buffer to point it at
        if (va.va_active != 0) {
                /*
                 * If we're going to ask for va_name, allocate a buffer to point it at
@@ -1030,12 +1422,28 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
                         */
                        if ((al.commonattr & (ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID | ATTR_CMN_FILEID)) && !VATTR_IS_SUPPORTED(&va, va_linkid))
                                VATTR_CLEAR_ACTIVE(&va, va_linkid);     /* forget we wanted this */
                         */
                        if ((al.commonattr & (ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID | ATTR_CMN_FILEID)) && !VATTR_IS_SUPPORTED(&va, va_linkid))
                                VATTR_CLEAR_ACTIVE(&va, va_linkid);     /* forget we wanted this */
+                       
                        /*
                        /*
-                        * Many (most?) filesystems don't know their parent object id.  We can get it the
-                        * hard way.
+                        * Many filesystems don't know their parent object id.
+                        * If necessary, attempt to derive it from the vnode.
                         */
                         */
-                       if ((al.commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)) && !VATTR_IS_SUPPORTED(&va, va_parentid))
-                               VATTR_CLEAR_ACTIVE(&va, va_parentid);
+                       if ((al.commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)) &&
+                           !VATTR_IS_SUPPORTED(&va, va_parentid)) {
+                               vnode_t dvp;
+       
+                               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(&va, va_fileid)) {
+                                               va.va_parentid = lva.va_fileid;
+                                               VATTR_SET_SUPPORTED(&va, va_parentid);
+                                       }
+                                       vnode_put(dvp);
+                               }
+                       }
                        /*
                         * And we can report datasize/alloc from total.
                         */
                        /*
                         * And we can report datasize/alloc from total.
                         */
@@ -1047,7 +1455,8 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
                        /*
                         * If we don't have an encoding, go with UTF-8
                         */
                        /*
                         * If we don't have an encoding, go with UTF-8
                         */
-                       if ((al.commonattr & ATTR_CMN_SCRIPT) && !VATTR_IS_SUPPORTED(&va, va_encoding))
+                       if ((al.commonattr & ATTR_CMN_SCRIPT) &&
+                           !VATTR_IS_SUPPORTED(&va, va_encoding) && !return_valid)
                                VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
 
                        /*
                                VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
 
                        /*
@@ -1064,11 +1473,21 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
                        
                        /* check again */
                        if (!VATTR_ALL_SUPPORTED(&va)) {
                        
                        /* check again */
                        if (!VATTR_ALL_SUPPORTED(&va)) {
-                               error = EINVAL;
-                               VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not get all requested file attributes");
-                               VFS_DEBUG(ctx, vp, "ATTRLIST -        have %016llx wanted %016llx missing %016llx",
-                                   va.va_supported, va.va_active, va.va_active & ~va.va_supported);
-                               goto out;
+                               if (return_valid) {
+                                       if (pack_invalid) {
+                                               /* Fix up valid mask for post processing */
+                                               getattrlist_fixupattrs(&ab.valid, &va);
+                                               
+                                               /* Force packing of everything asked for */
+                                               va.va_supported = va.va_active;
+                                       } else {
+                                               /* Adjust the requested attributes */
+                                               getattrlist_fixupattrs((attribute_set_t *)&al.commonattr, &va);
+                                       }
+                               } else {
+                                       error = EINVAL;
+                                       goto out;
+                               }
                        }
                }
        }
                        }
                }
        }
@@ -1076,16 +1495,20 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
        /*
         * Compute variable-space requirements.
         */
        /*
         * Compute variable-space requirements.
         */
-       varsize = 0;                    /* length count */
+       varsize = 0; /* length count */
+
+       /* We may need to fix up the name attribute if requested */
        if (al.commonattr & ATTR_CMN_NAME) {
                if (VATTR_IS_SUPPORTED(&va, va_name)) {
                        va.va_name[MAXPATHLEN-1] = '\0';        /* Ensure nul-termination */
                        cnp = va.va_name;
                        cnl = strlen(cnp);
        if (al.commonattr & ATTR_CMN_NAME) {
                if (VATTR_IS_SUPPORTED(&va, va_name)) {
                        va.va_name[MAXPATHLEN-1] = '\0';        /* Ensure nul-termination */
                        cnp = va.va_name;
                        cnl = strlen(cnp);
-               } else {
+               } 
+               else {
+                       /* Filesystem did not support getting the name */
                        if (vnode_isvroot(vp)) {
                                if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
                        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
                                        /* 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
@@ -1102,7 +1525,8 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
                                else {
                                        getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
                                }
                                else {
                                        getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
                                }
-                       } else {
+                       } 
+                       else {
                                cnp = vname = vnode_getname(vp);
                                cnl = 0;
                                if (cnp != NULL) {
                                cnp = vname = vnode_getname(vp);
                                cnl = 0;
                                if (cnp != NULL) {
@@ -1113,6 +1537,26 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
                varsize += roundup(cnl + 1, 4);
        }
 
                varsize += roundup(cnl + 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.
+        */
+       if (al.commonattr & ATTR_CMN_FULLPATH) {
+               int len = MAXPATHLEN;
+               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());
+               if (err) {
+                       error = err;
+                       goto out;
+               }
+               fullpathlen = 0;
+               if (fullpathptr){
+                       fullpathlen = strlen(fullpathptr);
+               }
+               varsize += roundup(fullpathlen+1, 4);
+       }
+
        /*
         * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
         *
        /*
         * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
         *
@@ -1120,10 +1564,21 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
         * user-space this is OK.
         */
        if ((al.commonattr & ATTR_CMN_EXTENDED_SECURITY) &&
         * user-space this is OK.
         */
        if ((al.commonattr & ATTR_CMN_EXTENDED_SECURITY) &&
-           VATTR_IS_SUPPORTED(&va, va_acl) &&
-           (va.va_acl != NULL))
-               varsize += roundup(KAUTH_FILESEC_SIZE(va.va_acl->acl_entrycount), 4);
-       
+                       VATTR_IS_SUPPORTED(&va, va_acl) &&
+                       (va.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 (va.va_acl->acl_entrycount == KAUTH_FILESEC_NOACL) {
+                       varsize += roundup((KAUTH_FILESEC_SIZE(0)), 4);
+               }
+               else {
+                       varsize += roundup ((KAUTH_FILESEC_SIZE(va.va_acl->acl_entrycount)), 4);
+               }
+       }
+
        /*
         * Allocate a target buffer for attribute results.
         *
        /*
         * Allocate a target buffer for attribute results.
         *
@@ -1132,7 +1587,8 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
         * don't result in a panic if the caller's buffer is too small..
         */
        ab.allocated = fixedsize + varsize;
         * don't result in a panic if the caller's buffer is too small..
         */
        ab.allocated = fixedsize + varsize;
-       if (ab.allocated > ATTR_MAX_BUFFER) {
+       /* Cast 'allocated' to an unsigned to verify allocation size */
+       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;
                error = ENOMEM;
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
                goto out;
@@ -1178,20 +1634,34 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
         * Pack results into the destination buffer.
         */
        ab.fixedcursor = ab.base + sizeof(uint32_t);
         * Pack results into the destination buffer.
         */
        ab.fixedcursor = ab.base + sizeof(uint32_t);
+       if (return_valid) {
+               ab.fixedcursor += sizeof (attribute_set_t);
+               bzero(&ab.actual, sizeof (ab.actual));
+       }
        ab.varcursor = ab.base + fixedsize;
        ab.needed = ab.allocated;
 
        /* common attributes **************************************************/
        ab.varcursor = ab.base + fixedsize;
        ab.needed = ab.allocated;
 
        /* common attributes **************************************************/
-       if (al.commonattr & ATTR_CMN_NAME)
+       if (al.commonattr & ATTR_CMN_NAME) {
                attrlist_pack_string(&ab, cnp, cnl);
                attrlist_pack_string(&ab, cnp, cnl);
-       if (al.commonattr & ATTR_CMN_DEVID)
+               ab.actual.commonattr |= ATTR_CMN_NAME;
+       }
+       if (al.commonattr & ATTR_CMN_DEVID) {
                ATTR_PACK4(ab, vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
                ATTR_PACK4(ab, vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
-       if (al.commonattr & ATTR_CMN_FSID)
+               ab.actual.commonattr |= ATTR_CMN_DEVID;
+       }
+       if (al.commonattr & ATTR_CMN_FSID) {
                ATTR_PACK8(ab, vp->v_mount->mnt_vfsstat.f_fsid);
                ATTR_PACK8(ab, vp->v_mount->mnt_vfsstat.f_fsid);
-       if (al.commonattr & ATTR_CMN_OBJTYPE)
-               ATTR_PACK4(ab, vp->v_type);
-       if (al.commonattr & ATTR_CMN_OBJTAG)
+               ab.actual.commonattr |= ATTR_CMN_FSID;
+       }
+       if (al.commonattr & ATTR_CMN_OBJTYPE) {
+               ATTR_PACK4(ab, vtype);
+               ab.actual.commonattr |= ATTR_CMN_OBJTYPE;
+       }
+       if (al.commonattr & ATTR_CMN_OBJTAG) {
                ATTR_PACK4(ab, vp->v_tag);
                ATTR_PACK4(ab, vp->v_tag);
+               ab.actual.commonattr |= ATTR_CMN_OBJTAG;
+       }
        if (al.commonattr & ATTR_CMN_OBJID) {
                fsobj_id_t f;
                /*
        if (al.commonattr & ATTR_CMN_OBJID) {
                fsobj_id_t f;
                /*
@@ -1207,6 +1677,7 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
                }
                f.fid_generation = 0;
                ATTR_PACK8(ab, f);
                }
                f.fid_generation = 0;
                ATTR_PACK8(ab, f);
+               ab.actual.commonattr |= ATTR_CMN_OBJID;
        }
        if (al.commonattr & ATTR_CMN_OBJPERMANENTID) {
                fsobj_id_t f;
        }
        if (al.commonattr & ATTR_CMN_OBJPERMANENTID) {
                fsobj_id_t f;
@@ -1223,94 +1694,53 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
                }
                f.fid_generation = 0;
                ATTR_PACK8(ab, f);
                }
                f.fid_generation = 0;
                ATTR_PACK8(ab, f);
+               ab.actual.commonattr |= ATTR_CMN_OBJPERMANENTID;
        }
        if (al.commonattr & ATTR_CMN_PAROBJID) {
                fsobj_id_t f;
        }
        if (al.commonattr & ATTR_CMN_PAROBJID) {
                fsobj_id_t f;
-               /*
-                * If the filesystem doesn't know the parent ID, we can
-                * try to get it via v->v_parent.  Don't need to worry
-                * about links here, as we dont allow hardlinks to
-                * directories.
-                */
-               if (VATTR_IS_SUPPORTED(&va, va_parentid)) {
-                       f.fid_objno = va.va_parentid;
-               } else {
-                       struct vnode_attr lva;
-                       vnode_t pvp;
-
-                       pvp = vnode_getparent(vp);
-
-                       if (pvp == NULLVP) {
-                               error = EINVAL;
-                               goto out;
-                       }
-                       VATTR_INIT(&lva);
-                       VATTR_WANTED(&lva, va_fileid);
-                       error = vnode_getattr(pvp, &lva, ctx);
-                       vnode_put(pvp);
 
 
-                       if (error != 0)
-                               goto out;
-                       f.fid_objno = lva.va_fileid;
-               }
+               f.fid_objno = va.va_parentid;  /* could be lossy here! */
                f.fid_generation = 0;
                ATTR_PACK8(ab, f);
                f.fid_generation = 0;
                ATTR_PACK8(ab, f);
+               ab.actual.commonattr |= ATTR_CMN_PAROBJID;
        }
        }
-       if (al.commonattr & ATTR_CMN_SCRIPT)
-               ATTR_PACK4(ab, va.va_encoding);
-       if (al.commonattr & ATTR_CMN_CRTIME)
+       if (al.commonattr & ATTR_CMN_SCRIPT) {
+               if (VATTR_IS_SUPPORTED(&va, va_encoding)) {
+                       ATTR_PACK4(ab, va.va_encoding);
+                       ab.actual.commonattr |= ATTR_CMN_SCRIPT;
+               } else if (!return_valid || pack_invalid) {
+                       ATTR_PACK4(ab, 0x7e);
+               }
+       }
+       if (al.commonattr & ATTR_CMN_CRTIME) {
                ATTR_PACK_TIME(ab, va.va_create_time, proc_is64);
                ATTR_PACK_TIME(ab, va.va_create_time, proc_is64);
-       if (al.commonattr & ATTR_CMN_MODTIME)
+               ab.actual.commonattr |= ATTR_CMN_CRTIME;
+       }
+       if (al.commonattr & ATTR_CMN_MODTIME) {
                ATTR_PACK_TIME(ab, va.va_modify_time, proc_is64);
                ATTR_PACK_TIME(ab, va.va_modify_time, proc_is64);
-       if (al.commonattr & ATTR_CMN_CHGTIME)
+               ab.actual.commonattr |= ATTR_CMN_MODTIME;
+       }
+       if (al.commonattr & ATTR_CMN_CHGTIME) {
                ATTR_PACK_TIME(ab, va.va_change_time, proc_is64);
                ATTR_PACK_TIME(ab, va.va_change_time, proc_is64);
-       if (al.commonattr & ATTR_CMN_ACCTIME)
+               ab.actual.commonattr |= ATTR_CMN_CHGTIME;
+       }
+       if (al.commonattr & ATTR_CMN_ACCTIME) {
                ATTR_PACK_TIME(ab, va.va_access_time, proc_is64);
                ATTR_PACK_TIME(ab, va.va_access_time, proc_is64);
-       if (al.commonattr & ATTR_CMN_BKUPTIME)
+               ab.actual.commonattr |= ATTR_CMN_ACCTIME;
+       }
+       if (al.commonattr & ATTR_CMN_BKUPTIME) {
                ATTR_PACK_TIME(ab, va.va_backup_time, proc_is64);
                ATTR_PACK_TIME(ab, va.va_backup_time, proc_is64);
-       if (al.commonattr & ATTR_CMN_FNDRINFO) {
-               uio_t   auio;
-               size_t  fisize;
-               char    uio_buf[UIO_SIZEOF(1)];
-
-               fisize = imin(32, ab.allocated - (ab.fixedcursor - ab.base));
-               if (fisize > 0) {
-                       if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, uio_buf, sizeof(uio_buf))) == NULL) {
-                               error = ENOMEM;
-                               goto out;
-                       } else {
-                               uio_addiov(auio, CAST_USER_ADDR_T(ab.fixedcursor), fisize);
-                               error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio, &fisize, XATTR_NOSECURITY, ctx);
-                               uio_free(auio);
-                       }
-                       if (error != 0) {
-                               if ((error == ENOATTR) || (error == ENOENT) || (error == ENOTSUP) || (error == EPERM)) {
-                                       VFS_DEBUG(ctx, vp, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
-                                       bzero(ab.fixedcursor, 32);
-                                       error = 0;
-                               } else {
-                                       VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: reading system.finderinfo attribute");
-                                       goto out;
-                               }
-                       }
-               } else {
-                       VFS_DEBUG(ctx, vp, "ATTRLIST - no room in caller buffer for FINDERINFO");
-               }
-               ab.fixedcursor += 32;
+               ab.actual.commonattr |= ATTR_CMN_BKUPTIME;
        }
        }
-       if (al.commonattr & ATTR_CMN_OWNERID)
-               ATTR_PACK4(ab, va.va_uid);
-       if (al.commonattr & ATTR_CMN_GRPID)
-               ATTR_PACK4(ab, va.va_gid);
-       if (al.commonattr & ATTR_CMN_ACCESSMASK)
-               ATTR_PACK4(ab, va.va_mode);
-       if (al.commonattr & ATTR_CMN_FLAGS)
-               ATTR_PACK4(ab, va.va_flags);
+       /*
+        * 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 (al.commonattr & ATTR_CMN_USERACCESS) {      /* this is expensive */
        if (al.commonattr & ATTR_CMN_USERACCESS) {      /* this is expensive */
-               uint32_t        perms = 0;
-               if (vnode_isdir(vp)) {
+               if (vtype == VDIR) {
                        if (vnode_authorize(vp, NULL,
                        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)
                                perms |= R_OK;
                                perms |= W_OK;
                        if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
                                perms |= R_OK;
@@ -1324,7 +1754,60 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
                        if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
                                perms |= X_OK;
                }
                        if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
                                perms |= X_OK;
                }
+       }
+       
+       if (al.commonattr & ATTR_CMN_FNDRINFO) {
+               uio_t   auio;
+               size_t  fisize = 32;
+               char    uio_buf[UIO_SIZEOF(1)];
 
 
+               if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
+                    uio_buf, sizeof(uio_buf))) == NULL) {
+                       error = ENOMEM;
+                       goto out;
+               }
+               uio_addiov(auio, CAST_USER_ADDR_T(ab.fixedcursor), fisize);
+               error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio,
+                                   &fisize, XATTR_NOSECURITY, ctx);
+               uio_free(auio);
+               /*
+                * Default to zeros if its not available,
+                * unless ATTR_CMN_RETURNED_ATTRS was requested.
+                */
+               if (error &&
+                   (!return_valid || pack_invalid) &&
+                   ((error == ENOATTR) || (error == ENOENT) ||
+                    (error == ENOTSUP) || (error == EPERM))) {
+                       VFS_DEBUG(ctx, vp, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
+                       bzero(ab.fixedcursor, 32);
+                       error = 0;
+               }
+               if (error == 0) {
+                       ab.fixedcursor += 32;
+                       ab.actual.commonattr |= ATTR_CMN_FNDRINFO;
+               } else if (!return_valid) {
+                       VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: reading system.finderinfo attribute");
+                       goto out;
+               }
+       }
+       if (al.commonattr & ATTR_CMN_OWNERID) {
+               ATTR_PACK4(ab, va.va_uid);
+               ab.actual.commonattr |= ATTR_CMN_OWNERID;
+       }
+       if (al.commonattr & ATTR_CMN_GRPID) {
+               ATTR_PACK4(ab, va.va_gid);
+               ab.actual.commonattr |= ATTR_CMN_GRPID;
+       }
+       if (al.commonattr & ATTR_CMN_ACCESSMASK) {
+               ATTR_PACK4(ab, va.va_mode);
+               ab.actual.commonattr |= ATTR_CMN_ACCESSMASK;
+       }
+       if (al.commonattr & ATTR_CMN_FLAGS) {
+               ATTR_PACK4(ab, va.va_flags);
+               ab.actual.commonattr |= ATTR_CMN_FLAGS;
+       }
+       /* We already obtain the user access, so just fill in the buffer here */
+       if (al.commonattr & ATTR_CMN_USERACCESS) {
 #if CONFIG_MACF
                /* 
                 * Rather than MAC preceding DAC, in this case we want
 #if CONFIG_MACF
                /* 
                 * Rather than MAC preceding DAC, in this case we want
@@ -1343,6 +1826,7 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
 #endif /* MAC */
                VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d", perms);
                ATTR_PACK4(ab, perms);
 #endif /* MAC */
                VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d", perms);
                ATTR_PACK4(ab, perms);
+               ab.actual.commonattr |= ATTR_CMN_USERACCESS;
        }
        if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
                if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL)) {
        }
        if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
                if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL)) {
@@ -1353,93 +1837,191 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
                        fsec.fsec_magic = KAUTH_FILESEC_MAGIC;
                        fsec.fsec_owner = kauth_null_guid;
                        fsec.fsec_group = kauth_null_guid;
                        fsec.fsec_magic = KAUTH_FILESEC_MAGIC;
                        fsec.fsec_owner = kauth_null_guid;
                        fsec.fsec_group = kauth_null_guid;
-                       attrlist_pack_variable2(&ab, &fsec, ((char *)&fsec.fsec_acl - (char *)&fsec), va.va_acl, KAUTH_ACL_COPYSIZE(va.va_acl));
-               } else {
+                       attrlist_pack_variable2(&ab, &fsec, __offsetof(struct kauth_filesec, fsec_acl), va.va_acl, KAUTH_ACL_COPYSIZE(va.va_acl));
+                       ab.actual.commonattr |= ATTR_CMN_EXTENDED_SECURITY;
+               } else if (!return_valid || pack_invalid) {
                        attrlist_pack_variable(&ab, NULL, 0);
                }
        }
        if (al.commonattr & ATTR_CMN_UUID) {
                        attrlist_pack_variable(&ab, NULL, 0);
                }
        }
        if (al.commonattr & ATTR_CMN_UUID) {
-               if (!VATTR_IS_SUPPORTED(&va, va_uuuid)) {
-                       ATTR_PACK(&ab, kauth_null_guid);
-               } else {
-                       ATTR_PACK(&ab, va.va_uuuid);
-               }
-       }
-       if (al.commonattr & ATTR_CMN_GRPUUID) {
-               if (!VATTR_IS_SUPPORTED(&va, va_guuid)) {
-                       ATTR_PACK(&ab, kauth_null_guid);
-               } else {
-                       ATTR_PACK(&ab, va.va_guuid);
-               }
-       }
+               if (VATTR_IS_SUPPORTED(&va, va_uuuid)) {
+                       ATTR_PACK(&ab, va.va_uuuid);
+                       ab.actual.commonattr |= ATTR_CMN_UUID;
+               } else if (!return_valid || pack_invalid) {
+                       ATTR_PACK(&ab, kauth_null_guid);
+               }
+       }
+       if (al.commonattr & ATTR_CMN_GRPUUID) {
+               if (VATTR_IS_SUPPORTED(&va, va_guuid)) {
+                       ATTR_PACK(&ab, va.va_guuid);
+                       ab.actual.commonattr |= ATTR_CMN_GRPUUID;
+               } else if (!return_valid || pack_invalid) {
+                       ATTR_PACK(&ab, kauth_null_guid);
+               }
+       }
        if (al.commonattr & ATTR_CMN_FILEID) {
                ATTR_PACK8(ab, va.va_fileid);
        if (al.commonattr & ATTR_CMN_FILEID) {
                ATTR_PACK8(ab, va.va_fileid);
+               ab.actual.commonattr |= ATTR_CMN_FILEID;
        }
        if (al.commonattr & ATTR_CMN_PARENTID) {
        }
        if (al.commonattr & ATTR_CMN_PARENTID) {
-               uint64_t fileid;
-               /*
-                * If the filesystem doesn't know the parent ID, we can
-                * try to get it via v->v_parent.
-                */
-               if (VATTR_IS_SUPPORTED(&va, va_parentid)) {
-                       fileid = va.va_parentid;
-               } else {
-                       struct vnode_attr lva;
-                       vnode_t pvp;
-
-                       pvp = vnode_getparent(vp);
-
-                       if (pvp == NULLVP) {
-                               error = EINVAL;
-                               goto out;
-                       }
-                       VATTR_INIT(&lva);
-                       VATTR_WANTED(&lva, va_fileid);
-                       error = vnode_getattr(pvp, &lva, ctx);
-                       vnode_put(pvp);
-                       
-                       if (error != 0)
-                               goto out;
-                       fileid = lva.va_fileid;
-               }
-               ATTR_PACK8(ab, fileid);
+               ATTR_PACK8(ab, va.va_parentid);
+               ab.actual.commonattr |= ATTR_CMN_PARENTID;
+       }
+       
+       if (al.commonattr & ATTR_CMN_FULLPATH) {
+               attrlist_pack_string (&ab, fullpathptr, fullpathlen);
+               ab.actual.commonattr |= ATTR_CMN_FULLPATH;
+       }
+    
+    if (al.commonattr & ATTR_CMN_ADDEDTIME) {
+               ATTR_PACK_TIME(ab, va.va_addedtime, proc_is64);
+               ab.actual.commonattr |= ATTR_CMN_ADDEDTIME;
        }
 
        }
 
-       /* directory attributes **************************************************/
-       if (vnode_isdir(vp)) {
-               if (al.dirattr & ATTR_DIR_LINKCOUNT)                    /* full count of entries */
+       /* directory attributes *********************************************/
+       if (al.dirattr && (vtype == VDIR)) {
+               if (al.dirattr & ATTR_DIR_LINKCOUNT) {  /* full count of entries */
                        ATTR_PACK4(ab, (uint32_t)va.va_dirlinkcount);
                        ATTR_PACK4(ab, (uint32_t)va.va_dirlinkcount);
-               if (al.dirattr & ATTR_DIR_ENTRYCOUNT)
+                       ab.actual.dirattr |= ATTR_DIR_LINKCOUNT;
+               }
+               if (al.dirattr & ATTR_DIR_ENTRYCOUNT) {
                        ATTR_PACK4(ab, (uint32_t)va.va_nchildren);
                        ATTR_PACK4(ab, (uint32_t)va.va_nchildren);
-               if (al.dirattr & ATTR_DIR_MOUNTSTATUS)
-                       ATTR_PACK_CAST(&ab, uint32_t, (vp->v_flag & VROOT) ? DIR_MNTSTATUS_MNTPOINT : 0);
+                       ab.actual.dirattr |= ATTR_DIR_ENTRYCOUNT;
+               }
+               if (al.dirattr & ATTR_DIR_MOUNTSTATUS) {
+                       uint32_t mntstat;
+
+                       mntstat = (vp->v_flag & VROOT) ? DIR_MNTSTATUS_MNTPOINT : 0;
+#if CONFIG_TRIGGERS
+                       /*
+                        * Report back on active vnode triggers
+                        * that can directly trigger a mount
+                        */
+                       if (vp->v_resolve &&
+                           !(vp->v_resolve->vr_flags & VNT_NO_DIRECT_MOUNT)) {
+                               mntstat |= DIR_MNTSTATUS_TRIGGER;
+                       }
+#endif
+                       ATTR_PACK4(ab, mntstat);
+                       ab.actual.dirattr |= ATTR_DIR_MOUNTSTATUS;
+               }
        }
 
        /* file attributes **************************************************/
        }
 
        /* file attributes **************************************************/
-       if (!vnode_isdir(vp)) {
-               if (al.fileattr & ATTR_FILE_LINKCOUNT)
+       if (al.fileattr && (vtype != VDIR)) {
+
+               size_t  rsize = 0;
+               uint64_t rlength = 0;
+               uint64_t ralloc = 0;
+               /* 
+                * Pre-fetch the rsrc attributes now so we only get them once.
+                * Fetch the resource fork size/allocation via xattr interface 
+                */
+               if (al.fileattr & (ATTR_FILE_TOTALSIZE | ATTR_FILE_ALLOCSIZE | ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE)) {
+                       if ((error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &rsize, XATTR_NOSECURITY, ctx)) != 0) {
+                               if ((error == ENOENT) || (error == ENOATTR) || (error == ENOTSUP) || (error == EPERM)|| (error == EACCES)) {
+                                       rsize = 0;
+                                       error = 0;
+                               } else {
+                                       goto out;
+                               }
+                       }
+                       rlength = rsize;
+
+                       if (al.fileattr & (ATTR_FILE_RSRCALLOCSIZE | ATTR_FILE_ALLOCSIZE)) {
+                               uint32_t  blksize = vp->v_mount->mnt_vfsstat.f_bsize;
+                               if (blksize == 0) {
+                                       blksize = 512;
+                               }
+                               ralloc = roundup(rsize, blksize);
+                       }
+               }
+
+               if (al.fileattr & ATTR_FILE_LINKCOUNT) {
                        ATTR_PACK4(ab, (uint32_t)va.va_nlink);
                        ATTR_PACK4(ab, (uint32_t)va.va_nlink);
-               if (al.fileattr & ATTR_FILE_TOTALSIZE)
-                       ATTR_PACK8(ab, va.va_total_size);
-               if (al.fileattr & ATTR_FILE_ALLOCSIZE)
-                       ATTR_PACK8(ab, va.va_total_alloc);
-               if (al.fileattr & ATTR_FILE_IOBLOCKSIZE)
+                       ab.actual.fileattr |= ATTR_FILE_LINKCOUNT;
+               }
+               /*
+                * Note the following caveats for the TOTALSIZE and ALLOCSIZE attributes: 
+                * We infer that if the filesystem does not support va_data_size or va_data_alloc
+                * it must not know about alternate forks.  So when we need to gather
+                * the total size or total alloc, it's OK to substitute the total size for 
+                * the data size below.  This is because it is likely a flat filesystem and we must
+                * be using AD files to store the rsrc fork and EAs.  
+                * 
+                * Additionally, note that getattrlist is barred from being called on 
+                * resource fork paths. (Search for CN_ALLOWRSRCFORK).  So if the filesystem does 
+                * support va_data_size, it is guaranteed to represent the data fork's size.  This 
+                * is an important distinction to make because when we call vnode_getattr on 
+                * an HFS resource fork vnode, to get the size, it will vend out the resource 
+                * fork's size (it only gets the size of the passed-in vnode).  
+                */
+               if (al.fileattr & ATTR_FILE_TOTALSIZE) {
+                       uint64_t totalsize = rlength;                   
+
+                       if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
+                               totalsize += va.va_data_size;
+                       } else {
+                               totalsize += va.va_total_size;
+                       }
+
+                       ATTR_PACK8(ab, totalsize);
+                       ab.actual.fileattr |= ATTR_FILE_TOTALSIZE;
+               }
+               if (al.fileattr & ATTR_FILE_ALLOCSIZE) {
+                       uint64_t totalalloc = ralloc;
+               
+                       /* 
+                        * If data_alloc is supported, then it must represent the 
+                        * data fork size.
+                        */
+                       if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
+                               totalalloc += va.va_data_alloc;
+                       }
+                       else {
+                               totalalloc += va.va_total_alloc;
+                       }
+       
+                       ATTR_PACK8(ab, totalalloc);
+                       ab.actual.fileattr |= ATTR_FILE_ALLOCSIZE;
+               }
+               if (al.fileattr & ATTR_FILE_IOBLOCKSIZE) {
                        ATTR_PACK4(ab, va.va_iosize);
                        ATTR_PACK4(ab, va.va_iosize);
-               if (al.fileattr & ATTR_FILE_CLUMPSIZE)
-                       ATTR_PACK4(ab, 0);     /* XXX value is deprecated */
+                       ab.actual.fileattr |= ATTR_FILE_IOBLOCKSIZE;
+               }
+               if (al.fileattr & ATTR_FILE_CLUMPSIZE) {
+                       if (!return_valid || pack_invalid) {
+                               ATTR_PACK4(ab, 0);     /* this value is deprecated */
+                               ab.actual.fileattr |= ATTR_FILE_CLUMPSIZE;
+                       }
+               }
                if (al.fileattr & ATTR_FILE_DEVTYPE) {
                if (al.fileattr & ATTR_FILE_DEVTYPE) {
+                       uint32_t dev;
+
                        if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
                        if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
-                               ATTR_PACK(&ab, vp->v_specinfo->si_rdev);
+                               if (vp->v_specinfo != NULL)
+                                       dev = vp->v_specinfo->si_rdev;
+                               else
+                                       dev = va.va_rdev;
                        } else {
                        } else {
-                               ATTR_PACK_CAST(&ab, uint32_t, 0);
+                               dev = 0;
                        }
                        }
+                       ATTR_PACK4(ab, dev);
+                       ab.actual.fileattr |= ATTR_FILE_DEVTYPE;
                }
                }
+               
+               /* 
+                * If the filesystem does not support datalength
+                * or dataallocsize, then we infer that totalsize and 
+                * totalalloc are substitutes.
+                */
                if (al.fileattr & ATTR_FILE_DATALENGTH) {
                        if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
                                ATTR_PACK8(ab, va.va_data_size);
                        } else {
                                ATTR_PACK8(ab, va.va_total_size);
                        }
                if (al.fileattr & ATTR_FILE_DATALENGTH) {
                        if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
                                ATTR_PACK8(ab, va.va_data_size);
                        } else {
                                ATTR_PACK8(ab, va.va_total_size);
                        }
+                       ab.actual.fileattr |= ATTR_FILE_DATALENGTH;
                }
                if (al.fileattr & ATTR_FILE_DATAALLOCSIZE) {
                        if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
                }
                if (al.fileattr & ATTR_FILE_DATAALLOCSIZE) {
                        if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
@@ -1447,40 +2029,25 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
                        } else {
                                ATTR_PACK8(ab, va.va_total_alloc);
                        }
                        } else {
                                ATTR_PACK8(ab, va.va_total_alloc);
                        }
+                       ab.actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
                }
                }
-               /* fetch resource fork size/allocation via xattr interface */
-               if (al.fileattr & (ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE)) {
-                       size_t  rsize;
-                       uint64_t rlength;
-
-                       if ((error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &rsize, XATTR_NOSECURITY, ctx)) != 0) {
-                               if ((error == ENOENT) || (error == ENOATTR) || (error == ENOTSUP) || (error == EPERM)) {
-                                       rsize = 0;
-                                       error = 0;
-                               } else {
-                                       goto out;
-                               }
-                       }
-                       if (al.fileattr & ATTR_FILE_RSRCLENGTH) {
-                               rlength = rsize;
-                               ATTR_PACK8(ab, rlength);
-                       }
-                       if (al.fileattr & ATTR_FILE_RSRCALLOCSIZE) {
-                               uint32_t  blksize = vp->v_mount->mnt_vfsstat.f_bsize;
-                               if (blksize == 0)
-                                       blksize = 512;
-                               rlength = roundup(rsize, blksize);
-                               ATTR_PACK8(ab, rlength);
-                       }
+               /* already got the resource fork size/allocation above */
+               if (al.fileattr & ATTR_FILE_RSRCLENGTH) {
+                       ATTR_PACK8(ab, rlength);
+                       ab.actual.fileattr |= ATTR_FILE_RSRCLENGTH;
+               }
+               if (al.fileattr & ATTR_FILE_RSRCALLOCSIZE) {
+                       ATTR_PACK8(ab, ralloc);
+                       ab.actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE;
                }
        }
                }
        }
-       
+
        /* diagnostic */
        /* diagnostic */
-       if ((ab.fixedcursor - ab.base) != fixedsize)
-               panic("packed field size mismatch; allocated %ld but packed %d for common %08x vol %08x",
-                   fixedsize, ab.fixedcursor - ab.base, al.commonattr, al.volattr);
-       if (ab.varcursor != (ab.base + ab.needed))
-               panic("packed variable field size mismatch; used %d but expected %ld", ab.varcursor - ab.base, ab.needed);
+       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), al.commonattr, al.volattr);
+       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.
 
        /*
         * In the compatible case, we report the smaller of the required and returned sizes.
@@ -1489,6 +2056,18 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
         * they gave us, so they can always check for truncation themselves.
         */
        *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
         * they gave us, so they can always check for truncation themselves.
         */
        *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
+
+       /* Return attribute set output if requested. */
+       if (return_valid) {
+               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.dirattr &= ab.valid.dirattr;
+                       ab.actual.fileattr &= ab.valid.fileattr;
+               }
+               bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual));
+       }
        
        /* Only actually copyout as much out as the user buffer can hold */
        error = copyout(ab.base, uap->attributeBuffer, imin(uap->bufferSize, ab.allocated));
        
        /* Only actually copyout as much out as the user buffer can hold */
        error = copyout(ab.base, uap->attributeBuffer, imin(uap->bufferSize, ab.allocated));
@@ -1496,10 +2075,10 @@ getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
 out:
        if (va.va_name)
                kfree(va.va_name, MAXPATHLEN);
 out:
        if (va.va_name)
                kfree(va.va_name, MAXPATHLEN);
+       if (fullpathptr)
+               kfree(fullpathptr, MAXPATHLEN);
        if (vname)
        if (vname)
-               vnode_putname(vname);
-       if (vp)
-               vnode_put(vp);
+               vnode_putname(vname);
        if (ab.base != NULL)
                FREE(ab.base, M_TEMP);
        if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL))
        if (ab.base != NULL)
                FREE(ab.base, M_TEMP);
        if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL))
@@ -1509,6 +2088,78 @@ out:
        return(error);
 }
 
        return(error);
 }
 
+int
+fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval)
+{
+       struct vfs_context *ctx;
+       vnode_t         vp = NULL;
+       int             error;
+       struct getattrlist_args ap;
+
+       ctx = vfs_context_current();
+       error = 0;
+
+       if ((error = file_vnode(uap->fd, &vp)) != 0)
+               return (error);
+
+       if ((error = vnode_getwithref(vp)) != 0) {
+               file_drop(uap->fd);
+               return(error);
+       }
+
+       ap.path = 0;
+       ap.alist = uap->alist;
+       ap.attributeBuffer = uap->attributeBuffer;
+       ap.bufferSize = uap->bufferSize;
+       ap.options = uap->options;
+
+       /* Default to using the vnode's name. */
+       error = getattrlist_internal(vp, &ap, NULL, p, ctx);
+
+       file_drop(uap->fd);
+       if (vp)
+               vnode_put(vp);
+
+       return error;
+}
+
+int
+getattrlist(proc_t p, struct getattrlist_args *uap, __unused int32_t *retval)
+{
+       struct vfs_context *ctx;
+       struct nameidata nd;
+       vnode_t         vp = NULL;
+       u_long          nameiflags;
+       int             error;
+
+       ctx = vfs_context_current();
+       error = 0;
+
+       /*
+        * Look up the file.
+        */
+       nameiflags = NOTRIGGER | AUDITVNPATH1;
+       if (!(uap->options & FSOPT_NOFOLLOW))
+               nameiflags |= FOLLOW;
+       NDINIT(&nd, LOOKUP, OP_GETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
+
+       if ((error = namei(&nd)) != 0) {        
+               /* vp is still uninitialized */
+               return error;
+       }
+
+       vp = nd.ni_vp;
+       /* Pass along our componentname to getattrlist_internal */
+       error = getattrlist_internal(vp, uap, &(nd.ni_cnd), p, ctx);
+       
+       /* Retain the namei reference until the getattrlist completes. */
+       nameidone(&nd);
+       if (vp)
+               vnode_put(vp);
+
+       return error;
+}
+
 static int
 attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
 {
 static int
 attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
 {
@@ -1526,12 +2177,15 @@ attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
 #define ATTR_UNPACK_TIME(v, is64)                              \
        do {                                                    \
                if (is64) {                                     \
 #define ATTR_UNPACK_TIME(v, is64)                              \
        do {                                                    \
                if (is64) {                                     \
-                       struct user_timespec us;                \
+                       struct user64_timespec us;              \
                        ATTR_UNPACK(us);                        \
                        v.tv_sec = us.tv_sec;                   \
                        v.tv_nsec = us.tv_nsec;                 \
                } else {                                        \
                        ATTR_UNPACK(us);                        \
                        v.tv_sec = us.tv_sec;                   \
                        v.tv_nsec = us.tv_nsec;                 \
                } else {                                        \
-                       ATTR_UNPACK(v);                         \
+                       struct user32_timespec us;              \
+                       ATTR_UNPACK(us);                        \
+                       v.tv_sec = us.tv_sec;                   \
+                       v.tv_nsec = us.tv_nsec;                 \
                }                                               \
        } while(0)
 
                }                                               \
        } while(0)
 
@@ -1539,26 +2193,18 @@ attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
 /*
  * Write attributes.
  */
 /*
  * Write attributes.
  */
-int
-setattrlist(proc_t p, struct setattrlist_args *uap, __unused register_t *retval)
+static int
+setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_context_t ctx)
 {
        struct attrlist al;
 {
        struct attrlist al;
-       struct vfs_context context, *ctx;
        struct vnode_attr va;
        struct attrreference ar;
        struct vnode_attr va;
        struct attrreference ar;
-       struct nameidata nd;
-       vnode_t         vp;
-       u_long          nameiflags;
        kauth_action_t  action;
        char            *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
        int             proc_is64, error;
        uint32_t        nace;
        kauth_filesec_t rfsec;
 
        kauth_action_t  action;
        char            *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
        int             proc_is64, error;
        uint32_t        nace;
        kauth_filesec_t rfsec;
 
-       context.vc_thread = current_thread();
-       context.vc_ucred = kauth_cred_get();
-       ctx = &context;
-       vp = NULL;
        user_buf = NULL;
        fndrinfo = NULL;
        volname = NULL;
        user_buf = NULL;
        fndrinfo = NULL;
        volname = NULL;
@@ -1566,19 +2212,6 @@ setattrlist(proc_t p, struct setattrlist_args *uap, __unused register_t *retval)
        proc_is64 = proc_is64bit(p);
        VATTR_INIT(&va);
        
        proc_is64 = proc_is64bit(p);
        VATTR_INIT(&va);
        
-
-       /*
-        * Look up the file.
-        */
-       nameiflags = 0;
-       if ((uap->options & FSOPT_NOFOLLOW) == 0)
-               nameiflags |= FOLLOW;
-       NDINIT(&nd, LOOKUP, nameiflags | AUDITVNPATH1, UIO_USERSPACE, uap->path, &context);
-       if ((error = namei(&nd)) != 0)
-               goto out;
-       vp = nd.ni_vp;
-       nameidone(&nd);
-
        /*
         * Fetch the attribute set and validate.
         */
        /*
         * Fetch the attribute set and validate.
         */
@@ -1613,6 +2246,21 @@ setattrlist(proc_t p, struct setattrlist_args *uap, __unused register_t *retval)
                }
        }
 
                }
        }
 
+       /*
+        * 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.
+        */
+       if (al.commonattr == 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
        /*
         * 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
@@ -1640,7 +2288,7 @@ setattrlist(proc_t p, struct setattrlist_args *uap, __unused register_t *retval)
        VFS_DEBUG(ctx, vp, "ATTRLIST - copied in %d bytes of user attributes to %p", uap->bufferSize, user_buf);
 
 #if CONFIG_MACF
        VFS_DEBUG(ctx, vp, "ATTRLIST - copied in %d bytes of user attributes to %p", uap->bufferSize, user_buf);
 
 #if CONFIG_MACF
-       error = mac_vnode_check_setattrlist(&context, vp, &al);
+       error = mac_vnode_check_setattrlist(ctx, vp, &al);
        if (error)
                goto out;
 #endif /* MAC */
        if (error)
                goto out;
 #endif /* MAC */
@@ -1709,9 +2357,15 @@ setattrlist(proc_t p, struct setattrlist_args *uap, __unused register_t *retval)
                 */
                cp = cursor;
                ATTR_UNPACK(ar);
                 */
                cp = cursor;
                ATTR_UNPACK(ar);
+               if (ar.attr_dataoffset < 0) {
+                       VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied", ar.attr_dataoffset);
+                       error = EINVAL;
+                       goto out;
+               }
+
                cp += ar.attr_dataoffset;
                rfsec = (kauth_filesec_t)cp;
                cp += ar.attr_dataoffset;
                rfsec = (kauth_filesec_t)cp;
-               if (((char *)(rfsec + 1) > 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 */
                    (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 */
@@ -1754,7 +2408,14 @@ setattrlist(proc_t p, struct setattrlist_args *uap, __unused register_t *retval)
        if (al.volattr & ATTR_VOL_INFO) {
                if (al.volattr & ATTR_VOL_NAME) {
                        volname = cursor;
        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)) {
+                               VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied (2) ", ar.attr_dataoffset);
+                               error = EINVAL;
+                               goto out;
+                       }
+
                        volname += ar.attr_dataoffset;
                        if ((volname + ar.attr_length) > bufend) {
                                error = EINVAL;
                        volname += ar.attr_dataoffset;
                        if ((volname + ar.attr_length) > bufend) {
                                error = EINVAL;
@@ -1778,7 +2439,7 @@ setattrlist(proc_t p, struct setattrlist_args *uap, __unused register_t *retval)
         * Validate and authorize.
         */
        action = 0;
         * Validate and authorize.
         */
        action = 0;
-       if ((va.va_active != 0LL) && ((error = vnode_authattr(vp, &va, &action, &context)) != 0)) {
+       if ((va.va_active != 0LL) && ((error = vnode_authattr(vp, &va, &action, ctx)) != 0)) {
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attribute changes refused: %d", error);
                goto out;
        }
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attribute changes refused: %d", error);
                goto out;
        }
@@ -1793,11 +2454,11 @@ setattrlist(proc_t p, struct setattrlist_args *uap, __unused register_t *retval)
                                goto out;
                        }
                } else {
                                goto out;
                        }
                } else {
-                       action |= KAUTH_VNODE_WRITE_ATTRIBUTES;
+                       action |= KAUTH_VNODE_WRITE_EXTATTRIBUTES;
                }
        }
 
                }
        }
 
-       if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, &context)) != 0)) {
+       if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, ctx)) != 0)) {
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorization failed");
                goto out;
        }
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorization failed");
                goto out;
        }
@@ -1823,7 +2484,7 @@ setattrlist(proc_t p, struct setattrlist_args *uap, __unused register_t *retval)
        /*
         * Write the attributes if we have any.
         */
        /*
         * Write the attributes if we have any.
         */
-       if ((va.va_active != 0LL) && ((error = vnode_setattr(vp, &va, &context)) != 0)) {
+       if ((va.va_active != 0LL) && ((error = vnode_setattr(vp, &va, ctx)) != 0)) {
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
                goto out;
        }
                VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
                goto out;
        }
@@ -1834,7 +2495,7 @@ setattrlist(proc_t p, struct setattrlist_args *uap, __unused register_t *retval)
        if (fndrinfo != NULL) {
                if (al.volattr & ATTR_VOL_INFO) {
                        if (vp->v_tag == VT_HFS) {
        if (fndrinfo != NULL) {
                if (al.volattr & ATTR_VOL_INFO) {
                        if (vp->v_tag == VT_HFS) {
-                               error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, &context);
+                               error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, ctx);
                                if (error != 0)
                                        goto out;
                        } else {
                                if (error != 0)
                                        goto out;
                        } else {
@@ -1878,10 +2539,71 @@ setattrlist(proc_t p, struct setattrlist_args *uap, __unused register_t *retval)
        /* all done and successful */
        
 out:
        /* all done and successful */
        
 out:
-       if (vp != NULL)
-               vnode_put(vp);
        if (user_buf != NULL)
                FREE(user_buf, M_TEMP);
        VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d", error);
        return(error);
 }
        if (user_buf != NULL)
                FREE(user_buf, M_TEMP);
        VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d", error);
        return(error);
 }
+
+int
+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;
+       int error = 0;
+
+       ctx = vfs_context_current();
+
+       /*
+        * Look up the file.
+        */
+       nameiflags = AUDITVNPATH1;
+       if ((uap->options & FSOPT_NOFOLLOW) == 0)
+               nameiflags |= FOLLOW;
+       NDINIT(&nd, LOOKUP, OP_SETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
+       if ((error = namei(&nd)) != 0)
+               goto out;
+       vp = nd.ni_vp;
+       nameidone(&nd);
+
+       error = setattrlist_internal(vp, uap, p, ctx);
+out:
+       if (vp != NULL)
+               vnode_put(vp);
+       return error;
+}
+
+int
+fsetattrlist(proc_t p, struct fsetattrlist_args *uap, __unused int32_t *retval)
+{
+       struct vfs_context *ctx;
+       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 = vnode_getwithref(vp)) != 0) {
+               file_drop(uap->fd);
+               return(error);
+       }
+
+       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);
+       file_drop(uap->fd);
+       if (vp != NULL)
+               vnode_put(vp);
+
+       return error;
+}
+