/*
- * Copyright (c) 1995-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 1995-2019 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
- *
+ *
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
- *
+ *
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
- *
+ *
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
- *
+ *
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
#include <sys/namei.h>
#include <sys/kernel.h>
#include <sys/stat.h>
+#include <sys/syslog.h>
#include <sys/vnode_internal.h>
#include <sys/mount_internal.h>
#include <sys/proc_internal.h>
+#include <sys/file_internal.h>
#include <sys/kauth.h>
#include <sys/uio_internal.h>
-#include <sys/malloc.h>
+#include <kern/kalloc.h>
#include <sys/attr.h>
#include <sys/sysproto.h>
#include <sys/xattr.h>
#include <sys/fsevents.h>
-#include <kern/kalloc.h>
+#include <kern/zalloc.h>
#include <miscfs/specfs/specdev.h>
-#include <hfs/hfs.h>
+#include <security/audit/audit.h>
#if CONFIG_MACF
#include <security/mac_framework.h>
#endif
-#define ATTR_TIME_SIZE -1
+#define ATTR_TIME_SIZE -1
+
+static int readdirattr(vnode_t, struct fd_vn_data *, uio_t, struct attrlist *,
+ uint64_t, int *, int *, vfs_context_t ctx) __attribute__((noinline));
+
+static void
+vattr_get_alt_data(vnode_t, struct attrlist *, struct vnode_attr *, int, int,
+ int, vfs_context_t) __attribute__((noinline));
+
+static void get_error_attributes(vnode_t, struct attrlist *, uint64_t, user_addr_t,
+ size_t, int, caddr_t, vfs_context_t) __attribute__((noinline));
+
+static int getvolattrlist(vfs_context_t, vnode_t, struct attrlist *, user_addr_t,
+ size_t, uint64_t, enum uio_seg, int) __attribute__((noinline));
+
+static int get_direntry(vfs_context_t, vnode_t, struct fd_vn_data *, int *,
+ struct direntry **) __attribute__((noinline));
/*
* Structure describing the state of an in-progress attrlist operation.
*/
struct _attrlist_buf {
- char *base;
- char *fixedcursor;
- char *varcursor;
- ssize_t allocated;
+ char *base;
+ char *fixedcursor;
+ char *varcursor;
+ ssize_t allocated;
ssize_t needed;
- attribute_set_t actual;
+ attribute_set_t actual;
attribute_set_t valid;
};
/*
- * 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)
{
- ssize_t fit;
+ /*
+ * Use ssize_t for pointer math purposes,
+ * since a ssize_t is a signed long
+ */
+ 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);
+ }
- /* 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);
}
+
+/*
+ * 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
-attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t count, const void *ext, ssize_t extcount)
+attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t count,
+ const void *ext, ssize_t extcount)
{
- struct attrreference ar;
+ /* Use ssize_t's for pointer math ease */
+ struct attrreference ar;
ssize_t fit;
- /* pack the reference to the variable object */
- ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
- ar.attr_length = count + extcount;
+ /*
+ * 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 = (int32_t)(ab->varcursor - ab->fixedcursor);
+ ar.attr_length = (u_int32_t)(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 (source != NULL)
+ if (source != NULL) {
bcopy(source, 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 (ext != NULL)
+ /* Copy in data for the second attribute (if needed) if there is room */
+ if (ext != NULL) {
bcopy(ext, ab->varcursor, fit);
+ }
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);
}
+
+/*
+ * 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)
+attrlist_pack_string(struct _attrlist_buf *ab, const char *source, size_t count)
{
- struct attrreference ar;
+ struct attrreference ar;
ssize_t fit, space;
-
/*
* Supplied count is character count of string text, excluding trailing nul
* which we always supply here.
}
/*
- * 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;
+ ar.attr_dataoffset = (int32_t)(ab->varcursor - ab->fixedcursor);
+ ar.attr_length = (u_int32_t)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);
- fit = imin(count, space);
- if (fit > 0)
- bcopy(source, ab->varcursor, fit);
- /* is there room for our trailing nul? */
- if (space > fit)
- ab->varcursor[fit] = '\0';
+ fit = lmin(count, space);
+ if (space > 0) {
+ long bytes_to_zero;
- /* always move in increments of 4 */
- ab->varcursor += roundup(count + 1, 4);
+ /*
+ * 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);
+ /* 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 */
+ /*
+ * Zero out any additional bytes we might have as a
+ * result of rounding up.
+ */
+ bytes_to_zero = lmin((roundup(fit, 4) - fit),
+ space - fit);
+ if (bytes_to_zero) {
+ bzero(&(ab->varcursor[fit]), bytes_to_zero);
+ }
+ }
+ }
+ /*
+ * always move in increments of 4 (including the trailing NUL)
+ */
+ ab->varcursor += roundup((count + 1), 4);
}
#define ATTR_PACK4(AB, V) \
do { \
- if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 4) { \
- *(uint32_t *)AB.fixedcursor = V; \
- AB.fixedcursor += 4; \
- } \
+ if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 4) { \
+ *(uint32_t *)AB.fixedcursor = V; \
+ AB.fixedcursor += 4; \
+ } \
} while (0)
#define ATTR_PACK8(AB, V) \
do { \
- if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 8) { \
- *(uint64_t *)AB.fixedcursor = *(uint64_t *)&V; \
- AB.fixedcursor += 8; \
- } \
+ if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 8) { \
+ memcpy(AB.fixedcursor, &V, 8); \
+ AB.fixedcursor += 8; \
+ } \
} while (0)
-#define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
-#define ATTR_PACK_CAST(b, t, v) \
- do { \
- t _f = (t)v; \
- ATTR_PACK(b, _f); \
+#define ATTR_PACK(b, v) attrlist_pack_fixed(b, &v, sizeof(v))
+#define ATTR_PACK_CAST(b, t, v) \
+ do { \
+ t _f = (t)v; \
+ ATTR_PACK(b, _f); \
} while (0)
-#define ATTR_PACK_TIME(b, v, is64) \
- do { \
- if (is64) { \
- struct user64_timespec us = {v.tv_sec, v.tv_nsec}; \
- ATTR_PACK(&b, us); \
- } else { \
- struct user32_timespec us = {v.tv_sec, v.tv_nsec}; \
- ATTR_PACK(&b, us); \
- } \
+#define ATTR_PACK_TIME(b, v, is64) \
+ do { \
+ if (is64) { \
+ struct user64_timespec us = {.tv_sec = v.tv_sec, .tv_nsec = v.tv_nsec}; \
+ ATTR_PACK(&b, us); \
+ } else { \
+ struct user32_timespec us = {.tv_sec = (user32_time_t)v.tv_sec, .tv_nsec = (user32_long_t)v.tv_nsec}; \
+ ATTR_PACK(&b, us); \
+ } \
} while(0)
* Table-driven setup for all valid common/volume attributes.
*/
struct getvolattrlist_attrtab {
- attrgroup_t attr;
- uint64_t bits;
-#define VFSATTR_BIT(b) (VFSATTR_ ## b)
- ssize_t size;
+ attrgroup_t attr;
+ uint64_t bits;
+#define VFSATTR_BIT(b) (VFSATTR_ ## b)
+ ssize_t size;
};
static struct getvolattrlist_attrtab getvolattrlist_common_tab[] = {
- {ATTR_CMN_NAME, 0, sizeof(struct attrreference)},
- {ATTR_CMN_DEVID, 0, sizeof(dev_t)},
- {ATTR_CMN_FSID, 0, sizeof(fsid_t)},
- {ATTR_CMN_OBJTYPE, 0, sizeof(fsobj_type_t)},
- {ATTR_CMN_OBJTAG, 0, sizeof(fsobj_tag_t)},
- {ATTR_CMN_OBJID, 0, sizeof(fsobj_id_t)},
- {ATTR_CMN_OBJPERMANENTID, 0, sizeof(fsobj_id_t)},
- {ATTR_CMN_PAROBJID, 0, sizeof(fsobj_id_t)},
- {ATTR_CMN_SCRIPT, 0, sizeof(text_encoding_t)},
- {ATTR_CMN_CRTIME, VFSATTR_BIT(f_create_time), ATTR_TIME_SIZE},
- {ATTR_CMN_MODTIME, VFSATTR_BIT(f_modify_time), ATTR_TIME_SIZE},
- {ATTR_CMN_CHGTIME, VFSATTR_BIT(f_modify_time), ATTR_TIME_SIZE},
- {ATTR_CMN_ACCTIME, VFSATTR_BIT(f_access_time), ATTR_TIME_SIZE},
- {ATTR_CMN_BKUPTIME, VFSATTR_BIT(f_backup_time), ATTR_TIME_SIZE},
- {ATTR_CMN_FNDRINFO, 0, 32},
- {ATTR_CMN_OWNERID, 0, sizeof(uid_t)},
- {ATTR_CMN_GRPID, 0, sizeof(gid_t)},
- {ATTR_CMN_ACCESSMASK, 0, sizeof(uint32_t)},
- {ATTR_CMN_FLAGS, 0, sizeof(uint32_t)},
- {ATTR_CMN_USERACCESS, 0, sizeof(uint32_t)},
- {ATTR_CMN_EXTENDED_SECURITY, 0, sizeof(struct attrreference)},
- {ATTR_CMN_UUID, 0, sizeof(guid_t)},
- {ATTR_CMN_GRPUUID, 0, sizeof(guid_t)},
- {ATTR_CMN_FILEID, 0, sizeof(uint64_t)},
- {ATTR_CMN_PARENTID, 0, sizeof(uint64_t)},
- {ATTR_CMN_RETURNED_ATTRS, 0, sizeof(attribute_set_t)},
- {0, 0, 0}
+ {.attr = ATTR_CMN_NAME, .bits = 0, .size = sizeof(struct attrreference)},
+ {.attr = ATTR_CMN_DEVID, .bits = 0, .size = sizeof(dev_t)},
+ {.attr = ATTR_CMN_FSID, .bits = 0, .size = sizeof(fsid_t)},
+ {.attr = ATTR_CMN_OBJTYPE, .bits = 0, .size = sizeof(fsobj_type_t)},
+ {.attr = ATTR_CMN_OBJTAG, .bits = 0, .size = sizeof(fsobj_tag_t)},
+ {.attr = ATTR_CMN_OBJID, .bits = 0, .size = sizeof(fsobj_id_t)},
+ {.attr = ATTR_CMN_OBJPERMANENTID, .bits = 0, .size = sizeof(fsobj_id_t)},
+ {.attr = ATTR_CMN_PAROBJID, .bits = 0, .size = sizeof(fsobj_id_t)},
+ {.attr = ATTR_CMN_SCRIPT, .bits = 0, .size = sizeof(text_encoding_t)},
+ {.attr = ATTR_CMN_CRTIME, .bits = VFSATTR_BIT(f_create_time), .size = ATTR_TIME_SIZE},
+ {.attr = ATTR_CMN_MODTIME, .bits = VFSATTR_BIT(f_modify_time), .size = ATTR_TIME_SIZE},
+ {.attr = ATTR_CMN_CHGTIME, .bits = VFSATTR_BIT(f_modify_time), .size = ATTR_TIME_SIZE},
+ {.attr = ATTR_CMN_ACCTIME, .bits = VFSATTR_BIT(f_access_time), .size = ATTR_TIME_SIZE},
+ {.attr = ATTR_CMN_BKUPTIME, .bits = VFSATTR_BIT(f_backup_time), .size = ATTR_TIME_SIZE},
+ {.attr = ATTR_CMN_FNDRINFO, .bits = 0, .size = 32},
+ {.attr = ATTR_CMN_OWNERID, .bits = 0, .size = sizeof(uid_t)},
+ {.attr = ATTR_CMN_GRPID, .bits = 0, .size = sizeof(gid_t)},
+ {.attr = ATTR_CMN_ACCESSMASK, .bits = 0, .size = sizeof(uint32_t)},
+ {.attr = ATTR_CMN_FLAGS, .bits = 0, .size = sizeof(uint32_t)},
+ {.attr = ATTR_CMN_USERACCESS, .bits = 0, .size = sizeof(uint32_t)},
+ {.attr = ATTR_CMN_EXTENDED_SECURITY, .bits = 0, .size = sizeof(struct attrreference)},
+ {.attr = ATTR_CMN_UUID, .bits = 0, .size = sizeof(guid_t)},
+ {.attr = ATTR_CMN_GRPUUID, .bits = 0, .size = sizeof(guid_t)},
+ {.attr = ATTR_CMN_FILEID, .bits = 0, .size = sizeof(uint64_t)},
+ {.attr = ATTR_CMN_PARENTID, .bits = 0, .size = sizeof(uint64_t)},
+ {.attr = ATTR_CMN_RETURNED_ATTRS, .bits = 0, .size = sizeof(attribute_set_t)},
+ {.attr = ATTR_CMN_ERROR, .bits = 0, .size = sizeof(uint32_t)},
+ {.attr = 0, .bits = 0, .size = 0}
};
#define ATTR_CMN_VOL_INVALID \
(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID | \
ATTR_CMN_FILEID | ATTR_CMN_PARENTID)
static struct getvolattrlist_attrtab getvolattrlist_vol_tab[] = {
- {ATTR_VOL_FSTYPE, 0, sizeof(uint32_t)},
- {ATTR_VOL_SIGNATURE, VFSATTR_BIT(f_signature), sizeof(uint32_t)},
- {ATTR_VOL_SIZE, VFSATTR_BIT(f_blocks), sizeof(off_t)},
- {ATTR_VOL_SPACEFREE, VFSATTR_BIT(f_bfree) | VFSATTR_BIT(f_bsize), sizeof(off_t)},
- {ATTR_VOL_SPACEAVAIL, VFSATTR_BIT(f_bavail) | VFSATTR_BIT(f_bsize), sizeof(off_t)},
- {ATTR_VOL_MINALLOCATION, VFSATTR_BIT(f_bsize), sizeof(off_t)},
- {ATTR_VOL_ALLOCATIONCLUMP, VFSATTR_BIT(f_bsize), sizeof(off_t)},
- {ATTR_VOL_IOBLOCKSIZE, VFSATTR_BIT(f_iosize), sizeof(uint32_t)},
- {ATTR_VOL_OBJCOUNT, VFSATTR_BIT(f_objcount), sizeof(uint32_t)},
- {ATTR_VOL_FILECOUNT, VFSATTR_BIT(f_filecount), sizeof(uint32_t)},
- {ATTR_VOL_DIRCOUNT, VFSATTR_BIT(f_dircount), sizeof(uint32_t)},
- {ATTR_VOL_MAXOBJCOUNT, VFSATTR_BIT(f_maxobjcount), sizeof(uint32_t)},
- {ATTR_VOL_MOUNTPOINT, 0, sizeof(struct attrreference)},
- {ATTR_VOL_NAME, VFSATTR_BIT(f_vol_name), sizeof(struct attrreference)},
- {ATTR_VOL_MOUNTFLAGS, 0, sizeof(uint32_t)},
- {ATTR_VOL_MOUNTEDDEVICE, 0, sizeof(struct attrreference)},
- {ATTR_VOL_ENCODINGSUSED, 0, sizeof(uint64_t)},
- {ATTR_VOL_CAPABILITIES, VFSATTR_BIT(f_capabilities), sizeof(vol_capabilities_attr_t)},
- {ATTR_VOL_UUID, VFSATTR_BIT(f_uuid), sizeof(uuid_t)},
- {ATTR_VOL_ATTRIBUTES, VFSATTR_BIT(f_attributes), sizeof(vol_attributes_attr_t)},
- {ATTR_VOL_INFO, 0, 0},
- {0, 0, 0}
+ {.attr = ATTR_VOL_FSTYPE, .bits = 0, .size = sizeof(uint32_t)},
+ {.attr = ATTR_VOL_SIGNATURE, .bits = VFSATTR_BIT(f_signature), .size = sizeof(uint32_t)},
+ {.attr = ATTR_VOL_SIZE, .bits = VFSATTR_BIT(f_blocks) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+ {.attr = ATTR_VOL_SPACEFREE, .bits = VFSATTR_BIT(f_bfree) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+ {.attr = ATTR_VOL_SPACEAVAIL, .bits = VFSATTR_BIT(f_bavail) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+ {.attr = ATTR_VOL_MINALLOCATION, .bits = VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+ {.attr = ATTR_VOL_ALLOCATIONCLUMP, .bits = VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+ {.attr = ATTR_VOL_IOBLOCKSIZE, .bits = VFSATTR_BIT(f_iosize), .size = sizeof(uint32_t)},
+ {.attr = ATTR_VOL_OBJCOUNT, .bits = VFSATTR_BIT(f_objcount), .size = sizeof(uint32_t)},
+ {.attr = ATTR_VOL_FILECOUNT, .bits = VFSATTR_BIT(f_filecount), .size = sizeof(uint32_t)},
+ {.attr = ATTR_VOL_DIRCOUNT, .bits = VFSATTR_BIT(f_dircount), .size = sizeof(uint32_t)},
+ {.attr = ATTR_VOL_MAXOBJCOUNT, .bits = VFSATTR_BIT(f_maxobjcount), .size = sizeof(uint32_t)},
+ {.attr = ATTR_VOL_MOUNTPOINT, .bits = 0, .size = sizeof(struct attrreference)},
+ {.attr = ATTR_VOL_NAME, .bits = VFSATTR_BIT(f_vol_name), .size = sizeof(struct attrreference)},
+ {.attr = ATTR_VOL_MOUNTFLAGS, .bits = 0, .size = sizeof(uint32_t)},
+ {.attr = ATTR_VOL_MOUNTEDDEVICE, .bits = 0, .size = sizeof(struct attrreference)},
+ {.attr = ATTR_VOL_ENCODINGSUSED, .bits = 0, .size = sizeof(uint64_t)},
+ {.attr = ATTR_VOL_CAPABILITIES, .bits = VFSATTR_BIT(f_capabilities), .size = sizeof(vol_capabilities_attr_t)},
+ {.attr = ATTR_VOL_UUID, .bits = VFSATTR_BIT(f_uuid), .size = sizeof(uuid_t)},
+ {.attr = ATTR_VOL_QUOTA_SIZE, .bits = VFSATTR_BIT(f_quota) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+ {.attr = ATTR_VOL_RESERVED_SIZE, .bits = VFSATTR_BIT(f_reserved) | VFSATTR_BIT(f_bsize), .size = sizeof(off_t)},
+ {.attr = ATTR_VOL_ATTRIBUTES, .bits = VFSATTR_BIT(f_attributes), .size = sizeof(vol_attributes_attr_t)},
+ {.attr = ATTR_VOL_INFO, .bits = 0, .size = 0},
+ {.attr = 0, .bits = 0, .size = 0}
};
static int
getvolattrlist_parsetab(struct getvolattrlist_attrtab *tab, attrgroup_t attrs, struct vfs_attr *vsp,
- ssize_t *sizep, int is_64bit)
+ ssize_t *sizep, int is_64bit, unsigned int maxiter)
{
- attrgroup_t recognised;
+ attrgroup_t recognised;
recognised = 0;
do {
*sizep += tab->size;
}
}
- } while ((++tab)->attr != 0);
-
+ } while (((++tab)->attr != 0) && (--maxiter > 0));
+
/* check to make sure that we recognised all of the passed-in attributes */
- if (attrs & ~recognised)
- return(EINVAL);
- return(0);
+ if (attrs & ~recognised) {
+ return EINVAL;
+ }
+ return 0;
}
/*
static int
getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t *sizep, int is_64bit)
{
- int error;
+ int error;
+ if (!alp) {
+ return EINVAL;
+ }
/*
* Parse the above tables.
*/
- *sizep = sizeof(uint32_t); /* length count */
+ *sizep = sizeof(uint32_t); /* length count */
if (alp->commonattr) {
if ((alp->commonattr & ATTR_CMN_VOL_INVALID) &&
(alp->commonattr & ATTR_CMN_RETURNED_ATTRS) == 0) {
- return (EINVAL);
+ return EINVAL;
}
if ((error = getvolattrlist_parsetab(getvolattrlist_common_tab,
- alp->commonattr, vsp, sizep,
- is_64bit)) != 0) {
- return(error);
+ alp->commonattr, vsp, sizep,
+ is_64bit,
+ sizeof(getvolattrlist_common_tab) / sizeof(getvolattrlist_common_tab[0]))) != 0) {
+ return error;
}
}
if (alp->volattr &&
- (error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit)) != 0)
- return(error);
+ (error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit, sizeof(getvolattrlist_vol_tab) / sizeof(getvolattrlist_vol_tab[0]))) != 0) {
+ return error;
+ }
- return(0);
+ return 0;
}
/*
* Table-driven setup for all valid common/dir/file/fork attributes against files.
*/
struct getattrlist_attrtab {
- attrgroup_t attr;
- uint64_t bits;
-#define VATTR_BIT(b) (VNODE_ATTR_ ## b)
- ssize_t size;
- kauth_action_t action;
+ attrgroup_t attr;
+ uint64_t bits;
+#define VATTR_BIT(b) (VNODE_ATTR_ ## b)
+ ssize_t size;
+ kauth_action_t action;
};
-/*
- * A zero after the ATTR_ bit indicates that we don't expect the underlying FS to report back with this
+/*
+ * A zero after the ATTR_ bit indicates that we don't expect the underlying FS to report back with this
* information, and we will synthesize it at the VFS level.
*/
static struct getattrlist_attrtab getattrlist_common_tab[] = {
- {ATTR_CMN_NAME, VATTR_BIT(va_name), sizeof(struct attrreference), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_DEVID, 0, sizeof(dev_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_FSID, VATTR_BIT(va_fsid), sizeof(fsid_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_OBJTYPE, 0, sizeof(fsobj_type_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_OBJTAG, 0, sizeof(fsobj_tag_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_OBJID, VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_OBJPERMANENTID, VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_PAROBJID, VATTR_BIT(va_parentid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_SCRIPT, VATTR_BIT(va_encoding), sizeof(text_encoding_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_CRTIME, VATTR_BIT(va_create_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_MODTIME, VATTR_BIT(va_modify_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_CHGTIME, VATTR_BIT(va_change_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_ACCTIME, VATTR_BIT(va_access_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_BKUPTIME, VATTR_BIT(va_backup_time), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_FNDRINFO, 0, 32, KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_OWNERID, VATTR_BIT(va_uid), sizeof(uid_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_GRPID, VATTR_BIT(va_gid), sizeof(gid_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_ACCESSMASK, VATTR_BIT(va_mode), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_FLAGS, VATTR_BIT(va_flags), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_USERACCESS, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_EXTENDED_SECURITY, VATTR_BIT(va_acl), sizeof(struct attrreference), KAUTH_VNODE_READ_SECURITY},
- {ATTR_CMN_UUID, VATTR_BIT(va_uuuid), sizeof(guid_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_GRPUUID, VATTR_BIT(va_guuid), sizeof(guid_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_FILEID, VATTR_BIT(va_fileid), sizeof(uint64_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_PARENTID, VATTR_BIT(va_parentid), sizeof(uint64_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_FULLPATH, 0, sizeof(struct attrreference), KAUTH_VNODE_READ_ATTRIBUTES },
- {ATTR_CMN_ADDEDTIME, VATTR_BIT(va_addedtime), ATTR_TIME_SIZE, KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_CMN_RETURNED_ATTRS, 0, sizeof(attribute_set_t), 0},
- {0, 0, 0, 0}
+ {.attr = ATTR_CMN_NAME, .bits = VATTR_BIT(va_name), .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_DEVID, .bits = VATTR_BIT(va_fsid), .size = sizeof(dev_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_FSID, .bits = VATTR_BIT(va_fsid64), .size = sizeof(fsid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_OBJTYPE, .bits = 0, .size = sizeof(fsobj_type_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_OBJTAG, .bits = 0, .size = sizeof(fsobj_tag_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_OBJID, .bits = VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), .size = sizeof(fsobj_id_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_OBJPERMANENTID, .bits = VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), .size = sizeof(fsobj_id_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_PAROBJID, .bits = VATTR_BIT(va_parentid), .size = sizeof(fsobj_id_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_SCRIPT, .bits = VATTR_BIT(va_encoding), .size = sizeof(text_encoding_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_CRTIME, .bits = VATTR_BIT(va_create_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_MODTIME, .bits = VATTR_BIT(va_modify_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_CHGTIME, .bits = VATTR_BIT(va_change_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_ACCTIME, .bits = VATTR_BIT(va_access_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_BKUPTIME, .bits = VATTR_BIT(va_backup_time), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_FNDRINFO, .bits = 0, .size = 32, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_OWNERID, .bits = VATTR_BIT(va_uid), .size = sizeof(uid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_GRPID, .bits = VATTR_BIT(va_gid), .size = sizeof(gid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_ACCESSMASK, .bits = VATTR_BIT(va_mode), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_FLAGS, .bits = VATTR_BIT(va_flags), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_GEN_COUNT, .bits = VATTR_BIT(va_write_gencount), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_DOCUMENT_ID, .bits = VATTR_BIT(va_document_id), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_USERACCESS, .bits = 0, .size = sizeof(uint32_t), .action = 0},
+ {.attr = ATTR_CMN_EXTENDED_SECURITY, .bits = VATTR_BIT(va_acl), .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_SECURITY},
+ {.attr = ATTR_CMN_UUID, .bits = VATTR_BIT(va_uuuid), .size = sizeof(guid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_GRPUUID, .bits = VATTR_BIT(va_guuid), .size = sizeof(guid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_FILEID, .bits = VATTR_BIT(va_fileid), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_PARENTID, .bits = VATTR_BIT(va_parentid), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_FULLPATH, .bits = 0, .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_ADDEDTIME, .bits = VATTR_BIT(va_addedtime), .size = ATTR_TIME_SIZE, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_RETURNED_ATTRS, .bits = 0, .size = sizeof(attribute_set_t), .action = 0},
+ {.attr = ATTR_CMN_ERROR, .bits = 0, .size = sizeof(uint32_t), .action = 0},
+ {.attr = ATTR_CMN_DATA_PROTECT_FLAGS, .bits = VATTR_BIT(va_dataprotect_class), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = 0, .bits = 0, .size = 0, .action = 0}
};
static struct getattrlist_attrtab getattrlist_dir_tab[] = {
- {ATTR_DIR_LINKCOUNT, VATTR_BIT(va_dirlinkcount), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_DIR_ENTRYCOUNT, VATTR_BIT(va_nchildren), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_DIR_MOUNTSTATUS, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {0, 0, 0, 0}
+ {.attr = ATTR_DIR_LINKCOUNT, .bits = VATTR_BIT(va_dirlinkcount), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_DIR_ENTRYCOUNT, .bits = VATTR_BIT(va_nchildren), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_DIR_MOUNTSTATUS, .bits = 0, .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_DIR_ALLOCSIZE, .bits = VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_DIR_IOBLOCKSIZE, .bits = VATTR_BIT(va_iosize), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_DIR_DATALENGTH, .bits = VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = 0, .bits = 0, .size = 0, .action = 0}
};
static struct getattrlist_attrtab getattrlist_file_tab[] = {
- {ATTR_FILE_LINKCOUNT, VATTR_BIT(va_nlink), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_FILE_TOTALSIZE, VATTR_BIT(va_total_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_FILE_ALLOCSIZE, VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_FILE_IOBLOCKSIZE, VATTR_BIT(va_iosize), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_FILE_DEVTYPE, VATTR_BIT(va_rdev), sizeof(dev_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_FILE_DATALENGTH, VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_FILE_DATAALLOCSIZE, VATTR_BIT(va_total_alloc)| VATTR_BIT(va_data_alloc), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_FILE_RSRCLENGTH, 0, sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {ATTR_FILE_RSRCALLOCSIZE, 0, sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
- {0, 0, 0, 0}
-};
+ {.attr = ATTR_FILE_LINKCOUNT, .bits = VATTR_BIT(va_nlink), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_FILE_TOTALSIZE, .bits = VATTR_BIT(va_total_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_FILE_ALLOCSIZE, .bits = VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_FILE_IOBLOCKSIZE, .bits = VATTR_BIT(va_iosize), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_FILE_CLUMPSIZE, .bits = 0, .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_FILE_DEVTYPE, .bits = VATTR_BIT(va_rdev), .size = sizeof(dev_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_FILE_DATALENGTH, .bits = VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_FILE_DATAALLOCSIZE, .bits = VATTR_BIT(va_total_alloc) | VATTR_BIT(va_data_alloc), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_FILE_RSRCLENGTH, .bits = 0, .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_FILE_RSRCALLOCSIZE, .bits = 0, .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = 0, .bits = 0, .size = 0, .action = 0}
+};
+
+//for forkattr bits repurposed as new common attributes
+static struct getattrlist_attrtab getattrlist_common_extended_tab[] = {
+ {.attr = ATTR_CMNEXT_RELPATH, .bits = 0, .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMNEXT_PRIVATESIZE, .bits = VATTR_BIT(va_private_size), .size = sizeof(off_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMNEXT_LINKID, .bits = VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMNEXT_NOFIRMLINKPATH, .bits = 0, .size = sizeof(struct attrreference), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMNEXT_REALDEVID, .bits = VATTR_BIT(va_devid), .size = sizeof(uint32_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMNEXT_REALFSID, .bits = VATTR_BIT(va_fsid64), .size = sizeof(fsid_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMNEXT_CLONEID, .bits = VATTR_BIT(va_clone_id), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMNEXT_EXT_FLAGS, .bits = VATTR_BIT(va_extflags), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMNEXT_RECURSIVE_GENCOUNT, .bits = VATTR_BIT(va_recursive_gencount), .size = sizeof(uint64_t), .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = 0, .bits = 0, .size = 0, .action = 0}
+};
+
+/*
+ * This table is for attributes which are only set from the getattrlistbulk(2)
+ * call. These attributes have already been set from the common, file and
+ * directory tables but the vattr bits have not been recorded. Since these
+ * vattr bits are only used from the bulk call, we have a seperate table for
+ * these.
+ * The sizes are not returned from here since the sizes have already been
+ * accounted from the common, file and directory tables.
+ */
+static struct getattrlist_attrtab getattrlistbulk_common_tab[] = {
+ {.attr = ATTR_CMN_DEVID, .bits = VATTR_BIT(va_devid), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_FSID, .bits = VATTR_BIT(va_fsid64), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_OBJTYPE, .bits = VATTR_BIT(va_objtype), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_OBJTAG, .bits = VATTR_BIT(va_objtag), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_CMN_USERACCESS, .bits = VATTR_BIT(va_user_access), .size = 0, .action = 0},
+ {.attr = ATTR_CMN_FNDRINFO, .bits = VATTR_BIT(va_finderinfo), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = 0, .bits = 0, .size = 0, .action = 0}
+};
+
+static struct getattrlist_attrtab getattrlistbulk_file_tab[] = {
+ {.attr = ATTR_FILE_RSRCLENGTH, .bits = VATTR_BIT(va_rsrc_length), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = ATTR_FILE_RSRCALLOCSIZE, .bits = VATTR_BIT(va_rsrc_alloc), .size = 0, .action = KAUTH_VNODE_READ_ATTRIBUTES},
+ {.attr = 0, .bits = 0, .size = 0, .action = 0}
+};
+
+static struct getattrlist_attrtab getattrlistbulk_common_extended_tab[] = {
+ /* getattrlist_parsetab() expects > 1 entries */
+ {.attr = 0, .bits = 0, .size = 0, .action = 0},
+ {.attr = 0, .bits = 0, .size = 0, .action = 0}
+};
/*
* The following are attributes that VFS can derive.
*
* A majority of them are the same attributes that are required for stat(2) and statfs(2).
*/
-#define VFS_DFLT_ATTR_VOL (ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE | \
- ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE | \
- ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION | \
- ATTR_VOL_ALLOCATIONCLUMP | ATTR_VOL_IOBLOCKSIZE | \
- ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS | \
- ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES | \
- ATTR_VOL_ATTRIBUTES | ATTR_VOL_ENCODINGSUSED)
-
-#define VFS_DFLT_ATTR_CMN (ATTR_CMN_NAME | ATTR_CMN_DEVID | \
- ATTR_CMN_FSID | ATTR_CMN_OBJTYPE | \
- ATTR_CMN_OBJTAG | ATTR_CMN_OBJID | \
- ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT | \
- ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | \
- ATTR_CMN_FNDRINFO | \
- ATTR_CMN_OWNERID | ATTR_CMN_GRPID | \
- ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | \
- ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
- ATTR_CMN_PARENTID | ATTR_CMN_RETURNED_ATTRS)
-
-#define VFS_DFLT_ATTR_DIR (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
-
-#define VFS_DFLT_ATTR_FILE (ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE | \
- ATTR_FILE_ALLOCSIZE | ATTR_FILE_IOBLOCKSIZE | \
- ATTR_FILE_DEVTYPE | ATTR_FILE_DATALENGTH | \
- ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_RSRCLENGTH | \
- ATTR_FILE_RSRCALLOCSIZE)
+#define VFS_DFLT_ATTR_VOL (ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE | \
+ ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE | ATTR_VOL_QUOTA_SIZE | ATTR_VOL_RESERVED_SIZE | \
+ ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION | \
+ ATTR_VOL_ALLOCATIONCLUMP | ATTR_VOL_IOBLOCKSIZE | \
+ ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS | \
+ ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES | \
+ ATTR_VOL_ATTRIBUTES | ATTR_VOL_ENCODINGSUSED)
+
+#define VFS_DFLT_ATTR_CMN (ATTR_CMN_NAME | ATTR_CMN_DEVID | \
+ ATTR_CMN_FSID | ATTR_CMN_OBJTYPE | \
+ ATTR_CMN_OBJTAG | ATTR_CMN_OBJID | \
+ ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT | \
+ ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | \
+ ATTR_CMN_FNDRINFO | \
+ ATTR_CMN_OWNERID | ATTR_CMN_GRPID | \
+ ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | \
+ ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
+ ATTR_CMN_PARENTID | ATTR_CMN_RETURNED_ATTRS | \
+ ATTR_CMN_DOCUMENT_ID | ATTR_CMN_GEN_COUNT | \
+ ATTR_CMN_DATA_PROTECT_FLAGS)
+
+#define VFS_DFLT_ATTR_CMN_EXT (ATTR_CMNEXT_PRIVATESIZE | ATTR_CMNEXT_LINKID | \
+ ATTR_CMNEXT_NOFIRMLINKPATH | ATTR_CMNEXT_REALDEVID | \
+ ATTR_CMNEXT_REALFSID | ATTR_CMNEXT_CLONEID | \
+ ATTR_CMNEXT_EXT_FLAGS)
+
+#define VFS_DFLT_ATTR_DIR (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
+
+#define VFS_DFLT_ATTR_FILE (ATTR_FILE_LINKCOUNT | ATTR_FILE_TOTALSIZE | \
+ ATTR_FILE_ALLOCSIZE | ATTR_FILE_IOBLOCKSIZE | \
+ ATTR_FILE_DEVTYPE | ATTR_FILE_DATALENGTH | \
+ ATTR_FILE_DATAALLOCSIZE | ATTR_FILE_RSRCLENGTH | \
+ ATTR_FILE_RSRCALLOCSIZE)
static int
-getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs, struct vnode_attr *vap,
- ssize_t *sizep, kauth_action_t *actionp, int is_64bit)
+getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs,
+ struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp,
+ int is_64bit, unsigned int maxiter)
{
- attrgroup_t recognised;
-
+ attrgroup_t recognised;
recognised = 0;
+ if (!tab) {
+ return EINVAL;
+ }
+
do {
/* is this attribute set? */
if (tab->attr & attrs) {
recognised |= tab->attr;
- vap->va_active |= tab->bits;
- if (tab->size == ATTR_TIME_SIZE) {
- if (is_64bit) {
- *sizep += sizeof(struct user64_timespec);
+ if (vap) {
+ vap->va_active |= tab->bits;
+ }
+ if (sizep) {
+ if (tab->size == ATTR_TIME_SIZE) {
+ if (is_64bit) {
+ *sizep += sizeof(
+ struct user64_timespec);
+ } else {
+ *sizep += sizeof(
+ struct user32_timespec);
+ }
} else {
- *sizep += sizeof(struct user32_timespec);
+ *sizep += tab->size;
}
- } else {
- *sizep += tab->size;
}
- *actionp |= tab->action;
- if (attrs == recognised)
+ if (actionp) {
+ *actionp |= tab->action;
+ }
+ if (attrs == recognised) {
break; /* all done, get out */
+ }
}
- } while ((++tab)->attr != 0);
-
+ } while (((++tab)->attr != 0) && (--maxiter > 0));
+
/* check to make sure that we recognised all of the passed-in attributes */
- if (attrs & ~recognised)
- return(EINVAL);
- return(0);
+ if (attrs & ~recognised) {
+ return EINVAL;
+ }
+ return 0;
}
/*
* the data from a filesystem.
*/
static int
-getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, int is_64bit, int isdir)
+getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, int is_64bit, int isdir, int use_fork)
{
- int error;
+ int error;
/*
* Parse the above tables.
*/
- *sizep = sizeof(uint32_t); /* length count */
+ *sizep = sizeof(uint32_t); /* length count */
*actionp = 0;
if (alp->commonattr &&
- (error = getattrlist_parsetab(getattrlist_common_tab, alp->commonattr, vap, sizep, actionp, is_64bit)) != 0)
- return(error);
+ (error = getattrlist_parsetab(getattrlist_common_tab, alp->commonattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_common_tab) / sizeof(getattrlist_common_tab[0]))) != 0) {
+ return error;
+ }
if (isdir && alp->dirattr &&
- (error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr, vap, sizep, actionp, is_64bit)) != 0)
- return(error);
+ (error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_dir_tab) / sizeof(getattrlist_dir_tab[0]))) != 0) {
+ return error;
+ }
if (!isdir && alp->fileattr &&
- (error = getattrlist_parsetab(getattrlist_file_tab, alp->fileattr, vap, sizep, actionp, is_64bit)) != 0)
- return(error);
+ (error = getattrlist_parsetab(getattrlist_file_tab, alp->fileattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_file_tab) / sizeof(getattrlist_file_tab[0]))) != 0) {
+ return error;
+ }
+ if (use_fork && alp->forkattr &&
+ (error = getattrlist_parsetab(getattrlist_common_extended_tab, alp->forkattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_common_extended_tab) / sizeof(getattrlist_common_extended_tab[0]))) != 0) {
+ return error;
+ }
+
+ return 0;
+}
+
+/*
+ * Given the attributes listed in alp, configure vap to request
+ * the data from a filesystem.
+ */
+static int
+getattrlist_setupvattr_all(struct attrlist *alp, struct vnode_attr *vap,
+ enum vtype obj_type, ssize_t *fixedsize, int is_64bit, int use_fork)
+{
+ int error = 0;
+
+ /*
+ * Parse the above tables.
+ */
+ if (fixedsize) {
+ *fixedsize = sizeof(uint32_t);
+ }
+ if (alp->commonattr) {
+ error = getattrlist_parsetab(getattrlist_common_tab,
+ alp->commonattr, vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlist_common_tab) / sizeof(getattrlist_common_tab[0]));
+
+ if (!error) {
+ /* Ignore any errrors from the bulk table */
+ (void)getattrlist_parsetab(getattrlistbulk_common_tab,
+ alp->commonattr, vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlistbulk_common_tab) / sizeof(getattrlistbulk_common_tab[0]));
+ }
+ }
+
+ if (!error && (obj_type == VNON || obj_type == VDIR) && alp->dirattr) {
+ error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr,
+ vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlist_dir_tab) / sizeof(getattrlist_dir_tab[0]));
+ }
+
+ if (!error && (obj_type != VDIR) && alp->fileattr) {
+ error = getattrlist_parsetab(getattrlist_file_tab,
+ alp->fileattr, vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlist_file_tab) / sizeof(getattrlist_file_tab[0]));
+
+ if (!error) {
+ /*Ignore any errors from the bulk table */
+ (void)getattrlist_parsetab(getattrlistbulk_file_tab,
+ alp->fileattr, vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlistbulk_file_tab) / sizeof(getattrlistbulk_file_tab[0]));
+ }
+ }
+
+ /* fork attributes are like extended common attributes if enabled*/
+ if (!error && use_fork && alp->forkattr) {
+ error = getattrlist_parsetab(getattrlist_common_extended_tab,
+ alp->forkattr, vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlist_common_extended_tab) / sizeof(getattrlist_common_extended_tab[0]));
+
+ if (!error) {
+ (void)getattrlist_parsetab(getattrlistbulk_common_extended_tab,
+ alp->forkattr, vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlistbulk_common_extended_tab) / sizeof(getattrlistbulk_common_extended_tab[0]));
+ }
+ }
+
+ return error;
+}
+
+int
+vfs_setup_vattr_from_attrlist(struct attrlist *alp, struct vnode_attr *vap,
+ enum vtype obj_vtype, ssize_t *attrs_fixed_sizep, vfs_context_t ctx)
+{
+ VATTR_INIT(vap);
- return(0);
+ // the caller passes us no options, we assume the caller wants the new fork
+ // attr behavior, hence the hardcoded 1
+ return getattrlist_setupvattr_all(alp, vap, obj_vtype,
+ attrs_fixed_sizep, IS_64BIT_PROCESS(vfs_context_proc(ctx)), 1);
}
+
+
+
/*
* 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)
+getattrlist_fixupattrs(attribute_set_t *asp, struct vnode_attr *vap, int use_fork)
{
struct getattrlist_attrtab *tab;
if (asp->commonattr) {
tab = getattrlist_common_tab;
do {
- /*
+ /*
* This if() statement is slightly confusing. We're trying to
- * iterate through all of the bits listed in the array
+ * iterate through all of the bits listed in the array
* getattr_common_tab, and see if the filesystem was expected
* to support it, and whether or not we need to do anything about this.
- *
+ *
* This array is full of structs that have 4 fields (attr, bits, size, action).
- * The first is used to store the ATTR_CMN_* bit that was being requested
+ * The first is used to store the ATTR_CMN_* bit that was being requested
* from userland. The second stores the VATTR_BIT corresponding to the field
* filled in vnode_attr struct. If it is 0, then we don't typically expect
* the filesystem to fill in this field. The third is the size of the field,
* and the fourth is the type of kauth actions needed.
*
- * So, for all of the ATTR_CMN bits listed in this array, we iterate through
+ * So, for all of the ATTR_CMN bits listed in this array, we iterate through
* them, and check to see if it was both passed down to the filesystem via the
* va_active bitfield, and whether or not we expect it to be emitted from
* the filesystem. If it wasn't supported, then we un-twiddle the bit and move
* 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) {
}
} while ((++tab)->attr != 0);
}
+ if (use_fork && asp->forkattr) {
+ tab = getattrlist_common_extended_tab;
+ do {
+ if ((tab->attr & asp->forkattr) &&
+ (tab->bits & vap->va_active) &&
+ (vap->va_supported & tab->bits) == 0) {
+ asp->forkattr &= ~tab->attr;
+ }
+ } while ((++tab)->attr != 0);
+ }
}
static int
setattrlist_setfinderinfo(vnode_t vp, char *fndrinfo, struct vfs_context *ctx)
{
- uio_t auio;
- char uio_buf[UIO_SIZEOF(1)];
- int error;
+ uio_t auio;
+ char uio_buf[UIO_SIZEOF(1)];
+ int error;
if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_WRITE, uio_buf, sizeof(uio_buf))) == NULL) {
error = ENOMEM;
#if CONFIG_FSE
if (error == 0 && need_fsevent(FSE_FINDER_INFO_CHANGED, vp)) {
- add_fsevent(FSE_FINDER_INFO_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
+ add_fsevent(FSE_FINDER_INFO_CHANGED, ctx, FSE_ARG_VNODE, vp, FSE_ARG_DONE);
}
#endif
- return (error);
+ return error;
}
static void
getattrlist_findnamecomp(const char *mn, const char **np, ssize_t *nl)
{
- int counting;
- const char *cp;
+ int counting;
+ const char *cp;
/*
* We're looking for the last sequence of non / characters, but
}
}
/* need to close run? */
- if (counting)
+ if (counting) {
*nl = cp - *np;
+ }
}
static int
-getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
- vfs_context_t ctx, int is_64bit)
+getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
+ user_addr_t attributeBuffer, size_t bufferSize, uint64_t options,
+ enum uio_seg segflg, int is_64bit)
{
struct vfs_attr vs;
struct vnode_attr va;
struct _attrlist_buf ab;
- int error;
- ssize_t fixedsize, varsize;
- const char *cnp = NULL; /* protected by ATTR_CMN_NAME */
- ssize_t cnl = 0; /* protected by ATTR_CMN_NAME */
- int release_str = 0;
- mount_t mnt;
- int return_valid;
- int pack_invalid;
+ int error;
+ ssize_t fixedsize, varsize;
+ const char *cnp = NULL; /* protected by ATTR_CMN_NAME */
+ ssize_t cnl = 0; /* protected by ATTR_CMN_NAME */
+ int release_str = 0;
+ mount_t mnt;
+ int return_valid;
+ int pack_invalid;
+ vnode_t root_vp = NULL;
ab.base = NULL;
VATTR_INIT(&va);
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);
+ pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS);
if (pack_invalid) {
/* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
if (!return_valid) {
goto out;
}
/* Keep invalid attrs from being uninitialized */
- bzero(&vs, sizeof (vs));
+ bzero(&vs, sizeof(vs));
/* Generate a valid mask for post processing */
- bcopy(&alp->commonattr, &ab.valid, sizeof (attribute_set_t));
+ bcopy(&alp->commonattr, &ab.valid, sizeof(attribute_set_t));
}
- /*
- * For now, the vnode must be the root of its filesystem.
- * To relax this, we need to be able to find the root vnode of a filesystem
- * from any vnode in the filesystem.
- */
+ /* If we do not have root vnode, look it up and substitute it in */
if (!vnode_isvroot(vp)) {
- error = EINVAL;
- VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
- goto out;
+ if (mnt != NULL) {
+ error = VFS_ROOT(mnt, &root_vp, ctx);
+ if (error) {
+ VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested on non-root vnode, but got an error getting root.");
+ goto out;
+ }
+ vp = root_vp;
+ } else {
+ error = EINVAL;
+ VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested on non-root vnode, but no backpointer to mount.");
+ goto out;
+ }
}
/*
if (vs.f_active != 0) {
/* If we're going to ask for f_vol_name, allocate a buffer to point it at */
if (VFSATTR_IS_ACTIVE(&vs, f_vol_name)) {
- vs.f_vol_name = (char *) kalloc(MAXPATHLEN);
- if (vs.f_vol_name == NULL) {
- error = ENOMEM;
- VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate f_vol_name buffer");
- goto out;
- }
+ vs.f_vol_name = (char *) zalloc(ZV_NAMEI);
+ vs.f_vol_name[0] = '\0';
}
-#if CONFIG_MACF
- error = mac_mount_check_getattr(ctx, mnt, &vs);
- if (error != 0)
- goto out;
-#endif
VFS_DEBUG(ctx, vp, "ATTRLIST - calling to get %016llx with supported %016llx", vs.f_active, vs.f_supported);
if ((error = vfs_getattr(mnt, &vs, ctx)) != 0) {
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
goto out;
}
-
+#if CONFIG_MACF
+ error = mac_mount_check_getattr(ctx, mnt, &vs);
+ if (error != 0) {
+ VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d", error);
+ goto out;
+ }
+#endif
/*
* Did we ask for something the filesystem doesn't support?
*/
if (!VFSATTR_ALL_SUPPORTED(&vs)) {
/* default value for volume subtype */
if (VFSATTR_IS_ACTIVE(&vs, f_fssubtype)
- && !VFSATTR_IS_SUPPORTED(&vs, f_fssubtype))
+ && !VFSATTR_IS_SUPPORTED(&vs, f_fssubtype)) {
VFSATTR_RETURN(&vs, f_fssubtype, 0);
+ }
/*
* If the file system didn't supply f_signature, then
* that most Carbon file systems should return.
*/
if (VFSATTR_IS_ACTIVE(&vs, f_signature)
- && !VFSATTR_IS_SUPPORTED(&vs, f_signature))
+ && !VFSATTR_IS_SUPPORTED(&vs, f_signature)) {
VFSATTR_RETURN(&vs, f_signature, 0x4244);
+ }
/* default for block size */
if (VFSATTR_IS_ACTIVE(&vs, f_bsize)
- && !VFSATTR_IS_SUPPORTED(&vs, f_bsize))
+ && !VFSATTR_IS_SUPPORTED(&vs, f_bsize)) {
VFSATTR_RETURN(&vs, f_bsize, mnt->mnt_devblocksize);
+ }
/* default value for volume f_attributes */
if (VFSATTR_IS_ACTIVE(&vs, f_attributes)
&& !VFSATTR_IS_SUPPORTED(&vs, f_attributes)) {
vol_attributes_attr_t *attrp = &vs.f_attributes;
-
+
attrp->validattr.commonattr = VFS_DFLT_ATTR_CMN;
attrp->validattr.volattr = VFS_DFLT_ATTR_VOL;
attrp->validattr.dirattr = VFS_DFLT_ATTR_DIR;
attrp->validattr.fileattr = VFS_DFLT_ATTR_FILE;
- attrp->validattr.forkattr = 0;
-
+ attrp->validattr.forkattr = VFS_DFLT_ATTR_CMN_EXT;
+
attrp->nativeattr.commonattr = 0;
attrp->nativeattr.volattr = 0;
attrp->nativeattr.dirattr = 0;
vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST;
vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
vs.f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
-
+
vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] = 0;
vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] = VOL_CAP_INT_ATTRLIST;
vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0;
vs.f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0;
VFSATTR_SET_SUPPORTED(&vs, f_capabilities);
- }
- else {
+ } else {
/* OR in VOL_CAP_INT_ATTRLIST if f_capabilities is supported */
vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST;
vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_ATTRLIST;
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 {
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp);
goto out;
}
-
+#if CONFIG_MACF
+ error = mac_vnode_check_getattr(ctx, NOCRED, vp, &va);
+ if (error != 0) {
+ VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d for root vnode", error);
+ goto out;
+ }
+#endif
if (VATTR_IS_ACTIVE(&va, va_encoding) &&
!VATTR_IS_SUPPORTED(&va, va_encoding)) {
- if (!return_valid || pack_invalid)
+ if (!return_valid || pack_invalid) {
/* use kTextEncodingMacUnicode */
VATTR_RETURN(&va, va_encoding, 0x7e);
- else
+ } else {
/* don't use a default */
alp->commonattr &= ~ATTR_CMN_SCRIPT;
+ }
}
}
varsize = 0;
if (alp->commonattr & ATTR_CMN_NAME) {
if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
- vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
+ vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
/* special case for boot volume. Use root name when it's
* available (which is the volume name) or just the mount on
* name of "/". we must do this for binary compatibility with
if (cnp == NULL) {
/* just use "/" as name */
cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
- }
- else {
+ } else {
release_str = 1;
}
cnl = strlen(cnp);
- }
- else {
+ } else {
getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
}
- if (alp->commonattr & ATTR_CMN_NAME)
+ if (alp->commonattr & ATTR_CMN_NAME) {
varsize += roundup(cnl + 1, 4);
+ }
}
- if (alp->volattr & ATTR_VOL_MOUNTPOINT)
+ if (alp->volattr & ATTR_VOL_MOUNTPOINT) {
varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntonname) + 1, 4);
+ }
if (alp->volattr & ATTR_VOL_NAME) {
- vs.f_vol_name[MAXPATHLEN-1] = '\0'; /* Ensure nul-termination */
+ vs.f_vol_name[MAXPATHLEN - 1] = '\0'; /* Ensure nul-termination */
varsize += roundup(strlen(vs.f_vol_name) + 1, 4);
}
- if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE)
+ if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE) {
varsize += roundup(strlen(mnt->mnt_vfsstat.f_mntfromname) + 1, 4);
+ }
/*
* Allocate a target buffer for attribute results.
* Note that since we won't ever copy out more than the caller requested,
* we never need to allocate more than they offer.
*/
- ab.allocated = imin(uap->bufferSize, fixedsize + varsize);
- if (ab.allocated > ATTR_MAX_BUFFER) {
+ ab.allocated = fixedsize + varsize;
+ if (((size_t)ab.allocated) > ATTR_MAX_BUFFER) {
error = ENOMEM;
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
goto out;
}
- MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_WAITOK);
+
+ if (return_valid &&
+ (ab.allocated < (ssize_t)(sizeof(uint32_t) + sizeof(attribute_set_t))) &&
+ !(options & FSOPT_REPORT_FULLSIZE)) {
+ uint32_t num_bytes_valid = sizeof(uint32_t);
+ /*
+ * Not enough to return anything and we don't have to report
+ * how much space is needed. Get out now.
+ * N.B. - We have only been called after having verified that
+ * attributeBuffer is at least sizeof(uint32_t);
+ */
+ if (UIO_SEG_IS_USER_SPACE(segflg)) {
+ error = copyout(&num_bytes_valid,
+ CAST_USER_ADDR_T(attributeBuffer), num_bytes_valid);
+ } else {
+ bcopy(&num_bytes_valid, (void *)attributeBuffer,
+ (size_t)num_bytes_valid);
+ }
+ goto out;
+ }
+
+ ab.base = kheap_alloc(KHEAP_TEMP, ab.allocated, Z_ZERO | Z_WAITOK);
if (ab.base == NULL) {
error = ENOMEM;
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
*/
ab.fixedcursor = ab.base + sizeof(uint32_t);
if (return_valid) {
- ab.fixedcursor += sizeof (attribute_set_t);
- bzero(&ab.actual, sizeof (ab.actual));
+ ab.fixedcursor += sizeof(attribute_set_t);
+ bzero(&ab.actual, sizeof(ab.actual));
}
ab.varcursor = ab.base + fixedsize;
ab.needed = fixedsize + varsize;
/* common attributes **************************************************/
+ if (alp->commonattr & ATTR_CMN_ERROR) {
+ ATTR_PACK4(ab, 0);
+ ab.actual.commonattr |= ATTR_CMN_ERROR;
+ }
if (alp->commonattr & ATTR_CMN_NAME) {
attrlist_pack_string(&ab, cnp, cnl);
ab.actual.commonattr |= ATTR_CMN_NAME;
ab.actual.commonattr |= ATTR_CMN_FSID;
}
if (alp->commonattr & ATTR_CMN_OBJTYPE) {
- if (!return_valid || pack_invalid)
+ if (!return_valid || pack_invalid) {
ATTR_PACK4(ab, 0);
+ }
}
if (alp->commonattr & ATTR_CMN_OBJTAG) {
ATTR_PACK4(ab, vp->v_tag);
ab.actual.commonattr |= ATTR_CMN_MODTIME;
}
if (alp->commonattr & ATTR_CMN_CHGTIME) {
- if (!return_valid || pack_invalid)
+ if (!return_valid || pack_invalid) {
ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
+ }
}
if (alp->commonattr & ATTR_CMN_ACCTIME) {
ATTR_PACK_TIME(ab, vs.f_access_time, is_64bit);
* This attribute isn't really Finder Info, at least for HFS.
*/
if (vp->v_tag == VT_HFS) {
+#define HFS_GET_BOOT_INFO (FCNTL_FS_SPECIFIC_BASE + 0x00004)
error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, (caddr_t)&f, 0, ctx);
if (error == 0) {
attrlist_pack_fixed(&ab, f, sizeof(f));
ATTR_PACK4(ab, va.va_flags);
ab.actual.commonattr |= ATTR_CMN_FLAGS;
}
- if (alp->commonattr & ATTR_CMN_USERACCESS) { /* XXX this is expensive and also duplicate work */
- uint32_t perms = 0;
+ if (alp->commonattr & ATTR_CMN_USERACCESS) { /* XXX this is expensive and also duplicate work */
+ uint32_t perms = 0;
if (vnode_isdir(vp)) {
if (vnode_authorize(vp, NULL,
- KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
+ KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0) {
perms |= W_OK;
- if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
+ }
+ if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0) {
perms |= R_OK;
- if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0)
+ }
+ if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0) {
perms |= X_OK;
+ }
} else {
- if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0)
+ if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0) {
perms |= W_OK;
- if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
+ }
+ if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0) {
perms |= R_OK;
- if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
+ }
+ if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0) {
perms |= X_OK;
+ }
}
#if CONFIG_MACF
- /*
+ /*
* Rather than MAC preceding DAC, in this case we want
* the smallest set of permissions granted by both MAC & DAC
* checks. We won't add back any permissions.
*/
- if (perms & W_OK)
- if (mac_vnode_check_access(ctx, vp, W_OK) != 0)
+ if (perms & W_OK) {
+ if (mac_vnode_check_access(ctx, vp, W_OK) != 0) {
perms &= ~W_OK;
- if (perms & R_OK)
- if (mac_vnode_check_access(ctx, vp, R_OK) != 0)
+ }
+ }
+ if (perms & R_OK) {
+ if (mac_vnode_check_access(ctx, vp, R_OK) != 0) {
perms &= ~R_OK;
- if (perms & X_OK)
- if (mac_vnode_check_access(ctx, vp, X_OK) != 0)
+ }
+ }
+ if (perms & X_OK) {
+ if (mac_vnode_check_access(ctx, vp, X_OK) != 0) {
perms &= ~X_OK;
+ }
+ }
#endif /* MAC */
KAUTH_DEBUG("ATTRLIST - returning user access %x", perms);
ATTR_PACK4(ab, perms);
if (pack_invalid) {
uint64_t fid = 0;
- if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY)
+ if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY) {
attrlist_pack_variable(&ab, NULL, 0);
- if (alp->commonattr & ATTR_CMN_UUID)
+ }
+ if (alp->commonattr & ATTR_CMN_UUID) {
ATTR_PACK(&ab, kauth_null_guid);
- if (alp->commonattr & ATTR_CMN_GRPUUID)
+ }
+ if (alp->commonattr & ATTR_CMN_GRPUUID) {
ATTR_PACK(&ab, kauth_null_guid);
- if (alp->commonattr & ATTR_CMN_FILEID)
+ }
+ if (alp->commonattr & ATTR_CMN_FILEID) {
ATTR_PACK8(ab, fid);
- if (alp->commonattr & ATTR_CMN_PARENTID)
+ }
+ if (alp->commonattr & ATTR_CMN_PARENTID) {
ATTR_PACK8(ab, fid);
+ }
}
/* volume attributes **************************************************/
ATTR_PACK_CAST(&ab, uint32_t, vfs_typenum(mnt));
ab.actual.volattr |= ATTR_VOL_FSTYPE;
}
- if (alp->volattr & ATTR_VOL_SIGNATURE) {
- ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature);
+ if (alp->volattr & ATTR_VOL_SIGNATURE) {
+ ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature);
ab.actual.volattr |= ATTR_VOL_SIGNATURE;
}
if (alp->volattr & ATTR_VOL_SIZE) {
ab.actual.volattr |= ATTR_VOL_MINALLOCATION;
}
if (alp->volattr & ATTR_VOL_ALLOCATIONCLUMP) {
- ATTR_PACK_CAST(&ab, off_t, vs.f_bsize); /* not strictly true */
+ ATTR_PACK_CAST(&ab, off_t, vs.f_bsize); /* not strictly true */
ab.actual.volattr |= ATTR_VOL_ALLOCATIONCLUMP;
}
if (alp->volattr & ATTR_VOL_IOBLOCKSIZE) {
ab.actual.volattr |= ATTR_VOL_MOUNTEDDEVICE;
}
if (alp->volattr & ATTR_VOL_ENCODINGSUSED) {
- if (!return_valid || pack_invalid)
+ if (!return_valid || pack_invalid) {
ATTR_PACK_CAST(&ab, uint64_t, ~0LL); /* return all encodings */
+ }
}
if (alp->volattr & ATTR_VOL_CAPABILITIES) {
/* fix up volume capabilities */
vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] &= ~VOL_CAP_INT_EXTENDED_SECURITY;
}
vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
+
+ /*
+ * if the filesystem doesn't mark either VOL_CAP_FMT_NO_IMMUTABLE_FILES
+ * or VOL_CAP_FMT_NO_PERMISSIONS as valid, assume they're not supported
+ */
+ if (!(vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_NO_IMMUTABLE_FILES)) {
+ vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~VOL_CAP_FMT_NO_IMMUTABLE_FILES;
+ vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_IMMUTABLE_FILES;
+ }
+
+ if (!(vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_NO_PERMISSIONS)) {
+ vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~VOL_CAP_FMT_NO_PERMISSIONS;
+ vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_PERMISSIONS;
+ }
+
+ /*
+ * ATTR_CMN_USERACCESS attribute was previously set by file-system drivers, thus volume capabilitiy
+ * VOL_CAP_INT_USERACCESS was conditionally enabled. ATTR_CMN_USERACCESS is now set inside VFS,
+ * regardless of underlying volume type thus we always set VOL_CAP_INT_USERACCESS.
+ */
+ vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_USERACCESS;
+ vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_USERACCESS;
+
ATTR_PACK(&ab, vs.f_capabilities);
ab.actual.volattr |= ATTR_VOL_CAPABILITIES;
}
ATTR_PACK(&ab, vs.f_uuid);
ab.actual.volattr |= ATTR_VOL_UUID;
}
+ if (alp->volattr & ATTR_VOL_QUOTA_SIZE) {
+ ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_quota);
+ ab.actual.volattr |= ATTR_VOL_QUOTA_SIZE;
+ }
+ if (alp->volattr & ATTR_VOL_RESERVED_SIZE) {
+ ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_reserved);
+ ab.actual.volattr |= ATTR_VOL_RESERVED_SIZE;
+ }
if (alp->volattr & ATTR_VOL_ATTRIBUTES) {
/* fix up volume attribute information */
ATTR_PACK(&ab, vs.f_attributes);
ab.actual.volattr |= ATTR_VOL_ATTRIBUTES;
}
-
+
/* diagnostic */
- if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize)
+ if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize) {
panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
fixedsize, (long) (ab.fixedcursor - ab.base), alp->commonattr, alp->volattr);
- if (!return_valid && ab.varcursor != (ab.base + ab.needed))
+ }
+ if (!return_valid && ab.varcursor != (ab.base + ab.needed)) {
panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab.varcursor - ab.base), ab.needed);
+ }
/*
* In the compatible case, we report the smaller of the required and returned sizes.
* of the result buffer, even if we copied less out. The caller knows how big a buffer
* they gave us, so they can always check for truncation themselves.
*/
- *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
-
+ *(uint32_t *)ab.base = (options & FSOPT_REPORT_FULLSIZE) ? (uint32_t)ab.needed : (uint32_t)lmin(bufferSize, ab.needed);
+
/* Return attribute set output if requested. */
- if (return_valid) {
+ if (return_valid &&
+ (ab.allocated >= (ssize_t)(sizeof(uint32_t) + sizeof(ab.actual)))) {
ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS;
if (pack_invalid) {
/* Only report the attributes that are valid */
ab.actual.commonattr &= ab.valid.commonattr;
ab.actual.volattr &= ab.valid.volattr;
}
- bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual));
+ bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof(ab.actual));
+ }
+
+ if (UIO_SEG_IS_USER_SPACE(segflg)) {
+ error = copyout(ab.base, CAST_USER_ADDR_T(attributeBuffer),
+ ulmin((uint32_t)bufferSize, (uint32_t)ab.needed));
+ } else {
+ bcopy(ab.base, (void *)attributeBuffer, (size_t)ulmin((uint32_t)bufferSize, (uint32_t)ab.needed));
}
- error = copyout(ab.base, uap->attributeBuffer, ab.allocated);
out:
- if (vs.f_vol_name != NULL)
- kfree(vs.f_vol_name, MAXPATHLEN);
+ if (vs.f_vol_name != NULL) {
+ zfree(ZV_NAMEI, vs.f_vol_name);
+ }
if (release_str) {
vnode_putname(cnp);
}
- if (ab.base != NULL)
- FREE(ab.base, M_TEMP);
+ kheap_free(KHEAP_TEMP, ab.base, ab.allocated);
VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
- return(error);
+
+ if (root_vp != NULL) {
+ vnode_put(root_vp);
+ }
+ return error;
}
/*
- * Obtain attribute information about a filesystem object.
+ * Pack ATTR_COMMON attributes into a user buffer.
+ * alp is a pointer to the bitmap of attributes required.
+ * abp is the state of the attribute filling operation.
+ * The attribute data (along with some other fields that are required
+ * are in ad.
*/
-
-static int
-getattrlist_internal(vnode_t vp, struct getattrlist_args *uap, proc_t p, vfs_context_t ctx)
+static errno_t
+attr_pack_common(vfs_context_t ctx, mount_t mp, vnode_t vp, struct attrlist *alp,
+ struct _attrlist_buf *abp, struct vnode_attr *vap, int proc_is64,
+ const char *cnp, ssize_t cnl, const char *fullpathptr,
+ ssize_t fullpathlen, int return_valid, int pack_invalid, int vtype,
+ int is_bulk)
{
- struct attrlist al;
- struct vnode_attr va;
- struct _attrlist_buf ab;
- 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;
- int return_valid;
- int pack_invalid;
- int vtype = 0;
- uint32_t perms = 0;
+ uint32_t perms = 0;
+ int error = 0;
+
+ if ((alp->commonattr & ATTR_CMN_ERROR) &&
+ (!return_valid || pack_invalid)) {
+ ATTR_PACK4((*abp), 0);
+ abp->actual.commonattr |= ATTR_CMN_ERROR;
+ }
+ if (alp->commonattr & ATTR_CMN_NAME) {
+ attrlist_pack_string(abp, cnp, cnl);
+ abp->actual.commonattr |= ATTR_CMN_NAME;
+ }
+ if (alp->commonattr & ATTR_CMN_DEVID) {
+ if (mp) { /* caller needs real devid */
+ ATTR_PACK4((*abp),
+ mp->mnt_vfsstat.f_fsid.val[0]);
+ abp->actual.commonattr |= ATTR_CMN_DEVID;
+ } else if (VATTR_IS_ACTIVE(vap, va_fsid) && VATTR_IS_SUPPORTED(vap, va_fsid)) {
+ ATTR_PACK4((*abp), vap->va_fsid);
+ abp->actual.commonattr |= ATTR_CMN_DEVID;
+ } else if (vp) {
+ ATTR_PACK4((*abp),
+ vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
+ abp->actual.commonattr |= ATTR_CMN_DEVID;
+ } else if (VATTR_IS_SUPPORTED(vap, va_devid)) {
+ ATTR_PACK4((*abp), vap->va_devid);
+ abp->actual.commonattr |= ATTR_CMN_DEVID;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_FSID) {
+ if (mp) { /* caller needs real fsid */
+ ATTR_PACK8((*abp),
+ mp->mnt_vfsstat.f_fsid);
+ abp->actual.commonattr |= ATTR_CMN_FSID;
+ } else if (VATTR_IS_SUPPORTED(vap, va_fsid64)) {
+ ATTR_PACK8((*abp), vap->va_fsid64);
+ abp->actual.commonattr |= ATTR_CMN_FSID;
+ } else if (vp) {
+ ATTR_PACK8((*abp),
+ vp->v_mount->mnt_vfsstat.f_fsid);
+ abp->actual.commonattr |= ATTR_CMN_FSID;
+ } else if (!return_valid || pack_invalid) {
+ fsid_t fsid = {{0}};
+ ATTR_PACK8((*abp), fsid);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_OBJTYPE) {
+ if (vp) {
+ ATTR_PACK4((*abp), vtype);
+ abp->actual.commonattr |= ATTR_CMN_OBJTYPE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_objtype)) {
+ ATTR_PACK4((*abp), vap->va_objtype);
+ abp->actual.commonattr |= ATTR_CMN_OBJTYPE;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_OBJTAG) {
+ if (vp) {
+ ATTR_PACK4((*abp), vp->v_tag);
+ abp->actual.commonattr |= ATTR_CMN_OBJTAG;
+ } else if (VATTR_IS_SUPPORTED(vap, va_objtag)) {
+ ATTR_PACK4((*abp), vap->va_objtag);
+ abp->actual.commonattr |= ATTR_CMN_OBJTAG;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_OBJID) {
+ /*
+ * Carbon can't deal with us reporting the target ID
+ * for links. So we ask the filesystem to give us the
+ * source ID as well, and if it gives us one, we use
+ * it instead.
+ */
+ if (vap->va_vaflags & VA_64BITOBJIDS) {
+ if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
+ ATTR_PACK8((*abp), vap->va_linkid);
+ } else {
+ ATTR_PACK8((*abp), vap->va_fileid);
+ }
+ } else {
+ fsobj_id_t f;
+ if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
+ f.fid_objno = (uint32_t)vap->va_linkid;
+ } else {
+ f.fid_objno = (uint32_t)vap->va_fileid;
+ }
+ f.fid_generation = 0;
+ ATTR_PACK8((*abp), f);
+ }
+ abp->actual.commonattr |= ATTR_CMN_OBJID;
+ }
+ if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
+ /*
+ * Carbon can't deal with us reporting the target ID
+ * for links. So we ask the filesystem to give us the
+ * source ID as well, and if it gives us one, we use
+ * it instead.
+ */
+ if (vap->va_vaflags & VA_64BITOBJIDS) {
+ if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
+ ATTR_PACK8((*abp), vap->va_linkid);
+ } else {
+ ATTR_PACK8((*abp), vap->va_fileid);
+ }
+ } else {
+ fsobj_id_t f;
+ if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
+ f.fid_objno = (uint32_t)vap->va_linkid;
+ } else {
+ f.fid_objno = (uint32_t)vap->va_fileid;
+ }
+ f.fid_generation = 0;
+ ATTR_PACK8((*abp), f);
+ }
+ abp->actual.commonattr |= ATTR_CMN_OBJPERMANENTID;
+ }
+ if (alp->commonattr & ATTR_CMN_PAROBJID) {
+ if (vap->va_vaflags & VA_64BITOBJIDS) {
+ ATTR_PACK8((*abp), vap->va_parentid);
+ } else {
+ fsobj_id_t f;
+ f.fid_objno = (uint32_t)vap->va_parentid;
+ f.fid_generation = 0;
+ ATTR_PACK8((*abp), f);
+ }
+ abp->actual.commonattr |= ATTR_CMN_PAROBJID;
+ }
+ if (alp->commonattr & ATTR_CMN_SCRIPT) {
+ if (VATTR_IS_SUPPORTED(vap, va_encoding)) {
+ ATTR_PACK4((*abp), vap->va_encoding);
+ abp->actual.commonattr |= ATTR_CMN_SCRIPT;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0x7e);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_CRTIME) {
+ ATTR_PACK_TIME((*abp), vap->va_create_time, proc_is64);
+ abp->actual.commonattr |= ATTR_CMN_CRTIME;
+ }
+ if (alp->commonattr & ATTR_CMN_MODTIME) {
+ ATTR_PACK_TIME((*abp), vap->va_modify_time, proc_is64);
+ abp->actual.commonattr |= ATTR_CMN_MODTIME;
+ }
+ if (alp->commonattr & ATTR_CMN_CHGTIME) {
+ ATTR_PACK_TIME((*abp), vap->va_change_time, proc_is64);
+ abp->actual.commonattr |= ATTR_CMN_CHGTIME;
+ }
+ if (alp->commonattr & ATTR_CMN_ACCTIME) {
+ ATTR_PACK_TIME((*abp), vap->va_access_time, proc_is64);
+ abp->actual.commonattr |= ATTR_CMN_ACCTIME;
+ }
+ if (alp->commonattr & ATTR_CMN_BKUPTIME) {
+ ATTR_PACK_TIME((*abp), vap->va_backup_time, proc_is64);
+ abp->actual.commonattr |= ATTR_CMN_BKUPTIME;
+ }
+ /*
+ * They are requesting user access, we should obtain this before getting
+ * the finder info. For some network file systems this is a performance
+ * improvement.
+ */
+ if (alp->commonattr & ATTR_CMN_USERACCESS) { /* this is expensive */
+ if (vp && !is_bulk) {
+ if (vtype == VDIR) {
+ if (vnode_authorize(vp, NULL,
+ KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE |
+ KAUTH_VNODE_ADD_SUBDIRECTORY |
+ KAUTH_VNODE_DELETE_CHILD, ctx) == 0) {
+ perms |= W_OK;
+ }
+
+ if (vnode_authorize(vp, NULL,
+ KAUTH_VNODE_ACCESS |
+ KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0) {
+ perms |= R_OK;
+ }
+
+ if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS |
+ KAUTH_VNODE_SEARCH, ctx) == 0) {
+ perms |= X_OK;
+ }
+ } else {
+ if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS |
+ KAUTH_VNODE_WRITE_DATA, ctx) == 0) {
+ perms |= W_OK;
+ }
+
+ if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0) {
+ perms |= R_OK;
+ }
+ if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0) {
+ perms |= X_OK;
+ }
+ }
+ } else if (is_bulk &&
+ VATTR_IS_SUPPORTED(vap, va_user_access)) {
+ perms = vap->va_user_access;
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_FNDRINFO) {
+ size_t fisize = 32;
+
+ error = 0;
+ if (vp && !is_bulk) {
+ uio_t auio;
+ char uio_buf[UIO_SIZEOF(1)];
+
+ if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE,
+ UIO_READ, uio_buf, sizeof(uio_buf))) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ uio_addiov(auio, CAST_USER_ADDR_T(abp->fixedcursor),
+ fisize);
+ /* fisize may be reset to 0 after this call */
+ 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(abp->fixedcursor, 32);
+ error = 0;
+ }
+
+ if (error == 0) {
+ abp->fixedcursor += 32;
+ abp->actual.commonattr |= ATTR_CMN_FNDRINFO;
+ } else if (!return_valid) {
+ goto out;
+ } else {
+ /*
+ * If we can inform the caller that we can't
+ * return this attribute, reset error and
+ * continue with the rest of the attributes.
+ */
+ error = 0;
+ }
+ } else if (VATTR_IS_SUPPORTED(vap, va_finderinfo)) {
+ bcopy(&vap->va_finderinfo[0], abp->fixedcursor, fisize);
+ abp->fixedcursor += fisize;
+ abp->actual.commonattr |= ATTR_CMN_FNDRINFO;
+ } else if (!return_valid || pack_invalid) {
+ bzero(abp->fixedcursor, fisize);
+ abp->fixedcursor += fisize;
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_OWNERID) {
+ ATTR_PACK4((*abp), vap->va_uid);
+ abp->actual.commonattr |= ATTR_CMN_OWNERID;
+ }
+ if (alp->commonattr & ATTR_CMN_GRPID) {
+ ATTR_PACK4((*abp), vap->va_gid);
+ abp->actual.commonattr |= ATTR_CMN_GRPID;
+ }
+ if (alp->commonattr & ATTR_CMN_ACCESSMASK) {
+ ATTR_PACK4((*abp), vap->va_mode);
+ abp->actual.commonattr |= ATTR_CMN_ACCESSMASK;
+ }
+ if (alp->commonattr & ATTR_CMN_FLAGS) {
+ ATTR_PACK4((*abp), vap->va_flags);
+ abp->actual.commonattr |= ATTR_CMN_FLAGS;
+ }
+ if (alp->commonattr & ATTR_CMN_GEN_COUNT) {
+ if (VATTR_IS_SUPPORTED(vap, va_write_gencount)) {
+ ATTR_PACK4((*abp), vap->va_write_gencount);
+ abp->actual.commonattr |= ATTR_CMN_GEN_COUNT;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+
+ if (alp->commonattr & ATTR_CMN_DOCUMENT_ID) {
+ if (VATTR_IS_SUPPORTED(vap, va_document_id)) {
+ ATTR_PACK4((*abp), vap->va_document_id);
+ abp->actual.commonattr |= ATTR_CMN_DOCUMENT_ID;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ /* We already obtain the user access, so just fill in the buffer here */
+ if (alp->commonattr & ATTR_CMN_USERACCESS) {
+#if CONFIG_MACF
+ if (!is_bulk && vp) {
+ /*
+ * Rather than MAC preceding DAC, in this case we want
+ * the smallest set of permissions granted by both MAC &
+ * DAC checks. We won't add back any permissions.
+ */
+ if (perms & W_OK) {
+ if (mac_vnode_check_access(ctx, vp, W_OK) != 0) {
+ perms &= ~W_OK;
+ }
+ }
+ if (perms & R_OK) {
+ if (mac_vnode_check_access(ctx, vp, R_OK) != 0) {
+ perms &= ~R_OK;
+ }
+ }
+ if (perms & X_OK) {
+ if (mac_vnode_check_access(ctx, vp, X_OK) != 0) {
+ perms &= ~X_OK;
+ }
+ }
+ }
+#endif /* MAC */
+ VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d", perms);
+ if (!is_bulk && vp) {
+ ATTR_PACK4((*abp), perms);
+ abp->actual.commonattr |= ATTR_CMN_USERACCESS;
+ } else if (is_bulk && VATTR_IS_SUPPORTED(vap, va_user_access)) {
+ ATTR_PACK4((*abp), perms);
+ abp->actual.commonattr |= ATTR_CMN_USERACCESS;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY) {
+ if (VATTR_IS_SUPPORTED(vap, va_acl) && (vap->va_acl != NULL)) {
+ struct kauth_filesec fsec;
+ /*
+ * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
+ */
+ fsec.fsec_magic = KAUTH_FILESEC_MAGIC;
+ fsec.fsec_owner = kauth_null_guid;
+ fsec.fsec_group = kauth_null_guid;
+ attrlist_pack_variable2(abp, &fsec, __offsetof(struct kauth_filesec, fsec_acl), vap->va_acl, KAUTH_ACL_COPYSIZE(vap->va_acl));
+ abp->actual.commonattr |= ATTR_CMN_EXTENDED_SECURITY;
+ } else if (!return_valid || pack_invalid) {
+ attrlist_pack_variable(abp, NULL, 0);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_UUID) {
+ if (VATTR_IS_SUPPORTED(vap, va_uuuid)) {
+ ATTR_PACK(abp, vap->va_uuuid);
+ abp->actual.commonattr |= ATTR_CMN_UUID;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK(abp, kauth_null_guid);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_GRPUUID) {
+ if (VATTR_IS_SUPPORTED(vap, va_guuid)) {
+ ATTR_PACK(abp, vap->va_guuid);
+ abp->actual.commonattr |= ATTR_CMN_GRPUUID;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK(abp, kauth_null_guid);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_FILEID) {
+ ATTR_PACK8((*abp), vap->va_fileid);
+ abp->actual.commonattr |= ATTR_CMN_FILEID;
+ }
+ if (alp->commonattr & ATTR_CMN_PARENTID) {
+ ATTR_PACK8((*abp), vap->va_parentid);
+ abp->actual.commonattr |= ATTR_CMN_PARENTID;
+ }
+
+ if (alp->commonattr & ATTR_CMN_FULLPATH) {
+ if (vp) {
+ attrlist_pack_string(abp, fullpathptr, fullpathlen);
+ abp->actual.commonattr |= ATTR_CMN_FULLPATH;
+ }
+ }
+
+ if (alp->commonattr & ATTR_CMN_ADDEDTIME) {
+ if (VATTR_IS_SUPPORTED(vap, va_addedtime)) {
+ ATTR_PACK_TIME((*abp), vap->va_addedtime, proc_is64);
+ abp->actual.commonattr |= ATTR_CMN_ADDEDTIME;
+ } else if (!return_valid || pack_invalid) {
+ struct timespec zerotime = {.tv_sec = 0, .tv_nsec = 0};
+
+ ATTR_PACK_TIME((*abp), zerotime, proc_is64);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) {
+ if (VATTR_IS_SUPPORTED(vap, va_dataprotect_class)) {
+ ATTR_PACK4((*abp), vap->va_dataprotect_class);
+ abp->actual.commonattr |= ATTR_CMN_DATA_PROTECT_FLAGS;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+out:
+ return error;
+}
+
+static errno_t
+attr_pack_dir(struct vnode *vp, struct attrlist *alp, struct _attrlist_buf *abp,
+ struct vnode_attr *vap, int return_valid, int pack_invalid)
+{
+ if (alp->dirattr & ATTR_DIR_LINKCOUNT) { /* full count of entries */
+ ATTR_PACK4((*abp), (uint32_t)vap->va_dirlinkcount);
+ abp->actual.dirattr |= ATTR_DIR_LINKCOUNT;
+ }
+ if (alp->dirattr & ATTR_DIR_ENTRYCOUNT) {
+ ATTR_PACK4((*abp), (uint32_t)vap->va_nchildren);
+ abp->actual.dirattr |= ATTR_DIR_ENTRYCOUNT;
+ }
+ if (alp->dirattr & ATTR_DIR_MOUNTSTATUS) {
+ uint32_t mntstat;
+
+ if (vp) {
+ /*
+ * The vnode that is passed down may either be a
+ * top level vnode of a mount stack or a mounted
+ * on vnode. In either case, the directory should
+ * be reported as a mount point.
+ */
+ if ((vp->v_flag & VROOT) || vnode_mountedhere(vp)) {
+ mntstat = DIR_MNTSTATUS_MNTPOINT;
+ } else {
+ mntstat = 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
+ } else {
+ mntstat = 0;
+ }
+
+ ATTR_PACK4((*abp), mntstat);
+ abp->actual.dirattr |= ATTR_DIR_MOUNTSTATUS;
+ }
+ if (alp->dirattr & ATTR_DIR_ALLOCSIZE) {
+ if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
+ ATTR_PACK8((*abp), vap->va_data_alloc);
+ abp->actual.dirattr |= ATTR_DIR_ALLOCSIZE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
+ ATTR_PACK8((*abp), vap->va_total_alloc);
+ abp->actual.dirattr |= ATTR_DIR_ALLOCSIZE;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+ if (alp->dirattr & ATTR_DIR_IOBLOCKSIZE) {
+ if (VATTR_IS_SUPPORTED(vap, va_iosize)) {
+ ATTR_PACK4((*abp), vap->va_iosize);
+ abp->actual.dirattr |= ATTR_DIR_IOBLOCKSIZE;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ /*
+ * If the filesystem does not support datalength
+ * or dataallocsize, then we infer that totalsize and
+ * totalalloc are substitutes.
+ */
+ if (alp->dirattr & ATTR_DIR_DATALENGTH) {
+ if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
+ ATTR_PACK8((*abp), vap->va_data_size);
+ abp->actual.dirattr |= ATTR_DIR_DATALENGTH;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
+ ATTR_PACK8((*abp), vap->va_total_size);
+ abp->actual.dirattr |= ATTR_DIR_DATALENGTH;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * The is_bulk parameter differentiates whether the function is called from
+ * getattrlist or getattrlistbulk. When coming in from getattrlistbulk,
+ * the corresponding va_* values are expected to be the values filled and no
+ * attempt is made to retrieve them by calling back into the filesystem.
+ */
+static errno_t
+attr_pack_file(vfs_context_t ctx, struct vnode *vp, struct attrlist *alp,
+ struct _attrlist_buf *abp, struct vnode_attr *vap, int return_valid,
+ int pack_invalid, int is_bulk)
+{
+ size_t rsize = 0;
+ uint64_t rlength = 0;
+ uint64_t ralloc = 0;
+ int error = 0;
+
+ /*
+ * Pre-fetch the rsrc attributes now so we only get them once.
+ * Fetch the resource fork size/allocation via xattr interface
+ */
+ if (vp && !is_bulk &&
+ (alp->fileattr & (ATTR_FILE_TOTALSIZE | ATTR_FILE_ALLOCSIZE |
+ ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE))) {
+ error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL,
+ &rsize, XATTR_NOSECURITY, ctx);
+ if (error) {
+ if ((error == ENOENT) || (error == ENOATTR) ||
+ (error == ENOTSUP) || (error == EPERM) ||
+ (error == EACCES)) {
+ rsize = 0;
+ error = 0;
+ } else {
+ goto out;
+ }
+ }
+ rlength = rsize;
+
+ if (alp->fileattr & (ATTR_FILE_RSRCALLOCSIZE |
+ ATTR_FILE_ALLOCSIZE)) {
+ uint32_t blksize;
+
+ blksize = vp->v_mount->mnt_vfsstat.f_bsize;
+
+ if (blksize == 0) {
+ blksize = 512;
+ }
+ ralloc = roundup(rsize, blksize);
+ }
+ }
+
+ if (alp->fileattr & ATTR_FILE_LINKCOUNT) {
+ ATTR_PACK4((*abp), (uint32_t)vap->va_nlink);
+ abp->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 (alp->fileattr & ATTR_FILE_TOTALSIZE) {
+ if (!is_bulk) {
+ uint64_t totalsize = rlength;
+
+ if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
+ totalsize += vap->va_data_size;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
+ totalsize += vap->va_total_size;
+ }
+
+ ATTR_PACK8((*abp), totalsize);
+ abp->actual.fileattr |= ATTR_FILE_TOTALSIZE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
+ ATTR_PACK8((*abp), vap->va_total_size);
+ abp->actual.fileattr |= ATTR_FILE_TOTALSIZE;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+ if (alp->fileattr & ATTR_FILE_ALLOCSIZE) {
+ if (!is_bulk) {
+ uint64_t totalalloc = ralloc;
+
+ /*
+ * If data_alloc is supported, then it must represent the
+ * data fork size.
+ */
+ if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
+ totalalloc += vap->va_data_alloc;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
+ totalalloc += vap->va_total_alloc;
+ }
+
+ ATTR_PACK8((*abp), totalalloc);
+ abp->actual.fileattr |= ATTR_FILE_ALLOCSIZE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
+ ATTR_PACK8((*abp), vap->va_total_alloc);
+ abp->actual.fileattr |= ATTR_FILE_ALLOCSIZE;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+ if (alp->fileattr & ATTR_FILE_IOBLOCKSIZE) {
+ if (VATTR_IS_SUPPORTED(vap, va_iosize)) {
+ ATTR_PACK4((*abp), vap->va_iosize);
+ abp->actual.fileattr |= ATTR_FILE_IOBLOCKSIZE;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ if (alp->fileattr & ATTR_FILE_CLUMPSIZE) {
+ if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0); /* this value is deprecated */
+ abp->actual.fileattr |= ATTR_FILE_CLUMPSIZE;
+ }
+ }
+ if (alp->fileattr & ATTR_FILE_DEVTYPE) {
+ if (vp && (vp->v_type == VCHR || vp->v_type == VBLK)) {
+ uint32_t dev;
+
+ if (vp->v_specinfo != NULL) {
+ dev = vp->v_specinfo->si_rdev;
+ } else if (VATTR_IS_SUPPORTED(vap, va_rdev)) {
+ dev = vap->va_rdev;
+ } else {
+ dev = 0;
+ }
+ ATTR_PACK4((*abp), dev);
+ abp->actual.fileattr |= ATTR_FILE_DEVTYPE;
+ } else if (vp) {
+ ATTR_PACK4((*abp), 0);
+ abp->actual.fileattr |= ATTR_FILE_DEVTYPE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_rdev)) {
+ ATTR_PACK4((*abp), vap->va_rdev);
+ abp->actual.fileattr |= ATTR_FILE_DEVTYPE;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ /*
+ * If the filesystem does not support datalength
+ * or dataallocsize, then we infer that totalsize and
+ * totalalloc are substitutes.
+ */
+ if (alp->fileattr & ATTR_FILE_DATALENGTH) {
+ if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
+ ATTR_PACK8((*abp), vap->va_data_size);
+ abp->actual.fileattr |= ATTR_FILE_DATALENGTH;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
+ ATTR_PACK8((*abp), vap->va_total_size);
+ abp->actual.fileattr |= ATTR_FILE_DATALENGTH;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+ if (alp->fileattr & ATTR_FILE_DATAALLOCSIZE) {
+ if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
+ ATTR_PACK8((*abp), vap->va_data_alloc);
+ abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
+ ATTR_PACK8((*abp), vap->va_total_alloc);
+ abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+ /* already got the resource fork size/allocation above */
+ if (alp->fileattr & ATTR_FILE_RSRCLENGTH) {
+ if (!is_bulk) {
+ ATTR_PACK8((*abp), rlength);
+ abp->actual.fileattr |= ATTR_FILE_RSRCLENGTH;
+ } else if (VATTR_IS_SUPPORTED(vap, va_rsrc_length)) {
+ ATTR_PACK8((*abp), vap->va_rsrc_length);
+ abp->actual.fileattr |= ATTR_FILE_RSRCLENGTH;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+ if (alp->fileattr & ATTR_FILE_RSRCALLOCSIZE) {
+ if (!is_bulk) {
+ ATTR_PACK8((*abp), ralloc);
+ abp->actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_rsrc_alloc)) {
+ ATTR_PACK8((*abp), vap->va_rsrc_alloc);
+ abp->actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+out:
+ return error;
+}
+
+/*
+ * Pack FORKATTR attributes into a user buffer.
+ * alp is a pointer to the bitmap of attributes required.
+ * abp is the state of the attribute filling operation.
+ * The attribute data (along with some other fields that are required
+ * are in ad.
+ */
+static errno_t
+attr_pack_common_extended(mount_t mp, struct vnode *vp, struct attrlist *alp,
+ struct _attrlist_buf *abp, const char *relpathptr, ssize_t relpathlen,
+ const char *REALpathptr, ssize_t REALpathlen,
+ struct vnode_attr *vap, int return_valid, int pack_invalid)
+{
+ if (vp && (alp->forkattr & ATTR_CMNEXT_RELPATH)) {
+ attrlist_pack_string(abp, relpathptr, relpathlen);
+ abp->actual.forkattr |= ATTR_CMNEXT_RELPATH;
+ }
+
+ if (alp->forkattr & ATTR_CMNEXT_PRIVATESIZE) {
+ if (VATTR_IS_SUPPORTED(vap, va_private_size)) {
+ ATTR_PACK8((*abp), vap->va_private_size);
+ abp->actual.forkattr |= ATTR_CMNEXT_PRIVATESIZE;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
- proc_is64 = proc_is64bit(p);
- VATTR_INIT(&va);
- va.va_name = NULL;
+ if (alp->forkattr & ATTR_CMNEXT_LINKID) {
+ uint64_t linkid;
+
+ if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
+ linkid = vap->va_linkid;
+ } else {
+ linkid = vap->va_fileid;
+ }
+
+ ATTR_PACK8((*abp), linkid);
+ abp->actual.forkattr |= ATTR_CMNEXT_LINKID;
+ }
+
+ if (vp && (alp->forkattr & ATTR_CMNEXT_NOFIRMLINKPATH)) {
+ attrlist_pack_string(abp, REALpathptr, REALpathlen);
+ abp->actual.forkattr |= ATTR_CMNEXT_NOFIRMLINKPATH;
+ }
+
+ if (alp->forkattr & ATTR_CMNEXT_REALDEVID) {
+ if (mp) {
+ ATTR_PACK4((*abp),
+ mp->mnt_vfsstat.f_fsid.val[0]);
+ abp->actual.forkattr |= ATTR_CMNEXT_REALDEVID;
+ } else if (vp) {
+ ATTR_PACK4((*abp),
+ vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
+ abp->actual.forkattr |= ATTR_CMNEXT_REALDEVID;
+ } else if (VATTR_IS_SUPPORTED(vap, va_fsid)) {
+ ATTR_PACK4((*abp), vap->va_fsid);
+ abp->actual.forkattr |= ATTR_CMN_DEVID;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+
+ if (alp->forkattr & ATTR_CMNEXT_REALFSID) {
+ if (mp) {
+ ATTR_PACK8((*abp),
+ mp->mnt_vfsstat.f_fsid);
+ abp->actual.forkattr |= ATTR_CMNEXT_REALFSID;
+ } else if (vp) {
+ ATTR_PACK8((*abp),
+ vp->v_mount->mnt_vfsstat.f_fsid);
+ abp->actual.forkattr |= ATTR_CMNEXT_REALFSID;
+ } else if (VATTR_IS_SUPPORTED(vap, va_fsid64)) {
+ ATTR_PACK8((*abp), vap->va_fsid64);
+ abp->actual.forkattr |= ATTR_CMN_FSID;
+ } else if (!return_valid || pack_invalid) {
+ fsid_t fsid = {{0}};
+
+ ATTR_PACK8((*abp), fsid);
+ }
+ }
+
+ if (alp->forkattr & ATTR_CMNEXT_CLONEID) {
+ if (VATTR_IS_SUPPORTED(vap, va_clone_id)) {
+ ATTR_PACK8((*abp), vap->va_clone_id);
+ abp->actual.forkattr |= ATTR_CMNEXT_CLONEID;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+
+ if (alp->forkattr & ATTR_CMNEXT_EXT_FLAGS) {
+ if (VATTR_IS_SUPPORTED(vap, va_extflags)) {
+ ATTR_PACK8((*abp), vap->va_extflags);
+ abp->actual.forkattr |= ATTR_CMNEXT_EXT_FLAGS;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+
+ if (alp->forkattr & ATTR_CMNEXT_RECURSIVE_GENCOUNT) {
+ if (VATTR_IS_SUPPORTED(vap, va_recursive_gencount)) {
+ ATTR_PACK8((*abp), vap->va_recursive_gencount);
+ abp->actual.forkattr |= ATTR_CMNEXT_RECURSIVE_GENCOUNT;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+
+ return 0;
+}
+
+static void
+vattr_get_alt_data(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
+ int return_valid, int is_bulk,
+#if !CONFIG_FIRMLINKS
+ __unused
+#endif
+ int is_realdev, vfs_context_t ctx)
+{
+ /*
+ * There are a couple of special cases.
+ * If we are after object IDs, we can make do with va_fileid.
+ */
+ if ((alp->commonattr &
+ (ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID | ATTR_CMN_FILEID)) &&
+ !VATTR_IS_SUPPORTED(vap, va_linkid)) {
+ /* forget we wanted this */
+ VATTR_CLEAR_ACTIVE(vap, va_linkid);
+ }
+
+ /*
+ * A filesystem may not support va_fsid64. If it is not available, then we'll
+ * synthesize it from the mount.
+ */
+ if ((alp->commonattr & ATTR_CMN_FSID) && !VATTR_IS_SUPPORTED(vap, va_fsid64)) {
+ VATTR_CLEAR_ACTIVE(vap, va_fsid64);
+ }
+
+ /* Same for fsid */
+ if ((alp->commonattr & ATTR_CMN_FSID) && !VATTR_IS_SUPPORTED(vap, va_fsid)) {
+ VATTR_CLEAR_ACTIVE(vap, va_fsid);
+ }
+
+ /* We request the fsid64 for the devid */
+ if ((alp->commonattr & ATTR_CMN_DEVID) && !VATTR_IS_SUPPORTED(vap, va_fsid)) {
+ VATTR_CLEAR_ACTIVE(vap, va_fsid);
+ }
+
+
+ /*
+ * Many filesystems don't know their parent object id.
+ * If necessary, attempt to derive it from the vnode.
+ */
+ if ((alp->commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)) && vp) {
+ vnode_t dvp;
+
+#if CONFIG_FIRMLINKS
+ /* If this is a firmlink target, we get the fileid of the firmlink parent. */
+ if (!is_realdev && (vp->v_flag & VFMLINKTARGET) && ((dvp = vp->v_fmlink) != NULL) && (vnode_get(dvp) == 0)) {
+ struct vnode_attr lva;
+
+ VATTR_INIT(&lva);
+ VATTR_WANTED(&lva, va_parentid);
+ VATTR_WANTED(&lva, va_fsid);
+ if (vnode_getattr(dvp, &lva, ctx) == 0 &&
+ VATTR_IS_SUPPORTED(&lva, va_parentid) &&
+ VATTR_IS_SUPPORTED(&lva, va_fsid) &&
+ (lva.va_fsid == (uint32_t)vp->v_mount->mnt_vfsstat.f_fsid.val[0])) {
+ vap->va_parentid = lva.va_parentid;
+ VATTR_SET_SUPPORTED(vap, va_parentid);
+ }
+ vnode_put(dvp);
+ } else
+#endif /* CONFIG_FIRMLINKS */
+ if (!VATTR_IS_SUPPORTED(vap, va_parentid) && !is_bulk) {
+ if ((dvp = vnode_getparent(vp)) != NULLVP) {
+ struct vnode_attr lva;
+
+ VATTR_INIT(&lva);
+ VATTR_WANTED(&lva, va_fileid);
+ if (vnode_getattr(dvp, &lva, ctx) == 0 &&
+ VATTR_IS_SUPPORTED(vap, va_fileid)) {
+ vap->va_parentid = lva.va_fileid;
+ VATTR_SET_SUPPORTED(vap, va_parentid);
+ }
+ vnode_put(dvp);
+ }
+ }
+ }
+
+ /*
+ * And we can report datasize/alloc from total.
+ */
+ if ((alp->fileattr & ATTR_FILE_DATALENGTH) &&
+ !VATTR_IS_SUPPORTED(vap, va_data_size)) {
+ VATTR_CLEAR_ACTIVE(vap, va_data_size);
+ }
+
+ if ((alp->fileattr & ATTR_FILE_DATAALLOCSIZE) &&
+ !VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
+ VATTR_CLEAR_ACTIVE(vap, va_data_alloc);
+ }
+
+ /*
+ * If we don't have an encoding, go with UTF-8
+ */
+ if ((alp->commonattr & ATTR_CMN_SCRIPT) &&
+ !VATTR_IS_SUPPORTED(vap, va_encoding) && !return_valid) {
+ VATTR_RETURN(vap, va_encoding,
+ 0x7e /* kTextEncodingMacUnicode */);
+ }
+
+ /*
+ * If we don't have a name, we'll get one from the vnode or
+ * mount point.
+ */
+ if ((alp->commonattr & ATTR_CMN_NAME) &&
+ !VATTR_IS_SUPPORTED(vap, va_name)) {
+ VATTR_CLEAR_ACTIVE(vap, va_name);
+ }
+
+ /* If va_dirlinkcount isn't supported use a default of 1. */
+ if ((alp->dirattr & ATTR_DIR_LINKCOUNT) &&
+ !VATTR_IS_SUPPORTED(vap, va_dirlinkcount)) {
+ VATTR_RETURN(vap, va_dirlinkcount, 1);
+ }
+}
+
+struct _attrlist_paths {
+ char *fullpathptr;
+ ssize_t *fullpathlenp;
+ char *relpathptr;
+ ssize_t *relpathlenp;
+ char *REALpathptr;
+ ssize_t *REALpathlenp;
+};
+
+static errno_t
+calc_varsize(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
+ ssize_t *varsizep, struct _attrlist_paths *pathsp, const char **vnamep,
+ const char **cnpp, ssize_t *cnlp)
+{
+ int error = 0;
+
+ *varsizep = 0; /* length count */
+ /* We may need to fix up the name attribute if requested */
+ if (alp->commonattr & ATTR_CMN_NAME) {
+ if (VATTR_IS_SUPPORTED(vap, va_name)) {
+ vap->va_name[MAXPATHLEN - 1] = '\0'; /* Ensure nul-termination */
+ *cnpp = vap->va_name;
+ *cnlp = strlen(*cnpp);
+ } else if (vp) {
+ /* Filesystem did not support getting the name */
+ if (vnode_isvroot(vp)) {
+ if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
+ vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
+ /* 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
+ * pre Tiger code. returning nothing for the boot volume name
+ * breaks installers - 3961058
+ */
+ *cnpp = *vnamep = vnode_getname(vp);
+ if (*cnpp == NULL) {
+ /* just use "/" as name */
+ *cnpp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
+ }
+ *cnlp = strlen(*cnpp);
+ } else {
+ getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, cnpp, cnlp);
+ }
+ } else {
+ *cnpp = *vnamep = vnode_getname(vp);
+ *cnlp = 0;
+ if (*cnpp != NULL) {
+ *cnlp = strlen(*cnpp);
+ }
+ }
+ } else {
+ *cnlp = 0;
+ }
+ *varsizep += roundup(*cnlp + 1, 4);
+ }
+
+ /*
+ * Compute the full path to this vnode, if necessary. This attribute is almost certainly
+ * not supported by any filesystem, so build the path to this vnode at this time.
+ */
+ if (vp && (alp->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, pathsp->fullpathptr, len, &len, 0, vfs_context_current());
+ if (err) {
+ error = err;
+ goto out;
+ }
+ if (pathsp->fullpathptr) {
+ *(pathsp->fullpathlenp) = strlen(pathsp->fullpathptr);
+ } else {
+ *(pathsp->fullpathlenp) = 0;
+ }
+ *varsizep += roundup(((*(pathsp->fullpathlenp)) + 1), 4);
+ }
+
+ /*
+ * Compute this vnode's volume relative path.
+ */
+ if (vp && (alp->forkattr & ATTR_CMNEXT_RELPATH)) {
+ int len;
+ int err;
+
+ /* call build_path making sure NOT to use the cache-only behavior */
+ err = build_path(vp, pathsp->relpathptr, MAXPATHLEN, &len, BUILDPATH_VOLUME_RELATIVE, vfs_context_current());
+ if (err) {
+ error = err;
+ goto out;
+ }
+
+ //`len' includes trailing null
+ *(pathsp->relpathlenp) = len - 1;
+ *varsizep += roundup(len, 4);
+ }
+
+ /*
+ * Compute this vnode's real (firmlink free) path.
+ */
+ if (vp && (alp->forkattr & ATTR_CMNEXT_NOFIRMLINKPATH)) {
+ int len;
+ int err;
+
+ /* call build_path making sure NOT to use the cache-only behavior */
+ err = build_path(vp, pathsp->REALpathptr, MAXPATHLEN, &len, BUILDPATH_NO_FIRMLINK, vfs_context_current());
+ if (err) {
+ error = err;
+ goto out;
+ }
+
+ //`len' includes trailing null
+ *(pathsp->REALpathlenp) = len - 1;
+ *varsizep += roundup(len, 4);
+ }
+
+ /*
+ * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
+ *
+ * XXX This needs to change at some point; since the blob is opaque in
+ * user-space this is OK.
+ */
+ if ((alp->commonattr & ATTR_CMN_EXTENDED_SECURITY) &&
+ VATTR_IS_SUPPORTED(vap, va_acl) &&
+ (vap->va_acl != NULL)) {
+ /*
+ * Since we have a kauth_acl_t (not a kauth_filesec_t), we have to check against
+ * KAUTH_FILESEC_NOACL ourselves
+ */
+ if (vap->va_acl->acl_entrycount == KAUTH_FILESEC_NOACL) {
+ *varsizep += roundup((KAUTH_FILESEC_SIZE(0)), 4);
+ } else {
+ *varsizep += roundup((KAUTH_FILESEC_SIZE(vap->va_acl->acl_entrycount)), 4);
+ }
+ }
+
+out:
+ return error;
+}
+
+static errno_t
+vfs_attr_pack_internal(mount_t mp, vnode_t vp, uio_t auio, struct attrlist *alp,
+ uint64_t options, struct vnode_attr *vap, __unused void *fndesc,
+ vfs_context_t ctx, int is_bulk, enum vtype vtype, ssize_t fixedsize)
+{
+ struct _attrlist_buf ab;
+ struct _attrlist_paths apaths = {.fullpathptr = NULL, .fullpathlenp = NULL,
+ .relpathptr = NULL, .relpathlenp = NULL,
+ .REALpathptr = NULL, .REALpathlenp = NULL};
+ ssize_t buf_size;
+ size_t copy_size;
+ ssize_t varsize;
+ const char *vname = NULL;
+ const char *cnp;
+ ssize_t cnl;
+ char *fullpathptr;
+ ssize_t fullpathlen;
+ char *relpathptr;
+ ssize_t relpathlen;
+ char *REALpathptr;
+ ssize_t REALpathlen;
+ int error;
+ int proc_is64;
+ int return_valid;
+ int pack_invalid;
+ int is_realdev;
+ int alloc_local_buf;
+ const int use_fork = options & FSOPT_ATTR_CMN_EXTENDED;
+
+ proc_is64 = proc_is64bit(vfs_context_proc(ctx));
ab.base = NULL;
cnp = "unknown";
cnl = 0;
fullpathptr = NULL;
fullpathlen = 0;
+ relpathptr = NULL;
+ relpathlen = 0;
+ REALpathptr = NULL;
+ REALpathlen = 0;
+ error = 0;
+ alloc_local_buf = 0;
+
+ buf_size = (ssize_t)uio_resid(auio);
+ if ((buf_size <= 0) || (uio_iovcnt(auio) > 1)) {
+ return EINVAL;
+ }
+
+ copy_size = 0;
+ /* Check for special packing semantics */
+ return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) ? 1 : 0;
+ pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS) ? 1 : 0;
+ is_realdev = options & FSOPT_RETURN_REALDEV ? 1 : 0;
+
+ if (pack_invalid) {
+ /* Generate a valid mask for post processing */
+ bcopy(&(alp->commonattr), &ab.valid, sizeof(attribute_set_t));
+ }
+
+ /* did we ask for something the filesystem doesn't support? */
+ if (vap->va_active &&
+ (!VATTR_ALL_SUPPORTED(vap)
+#if CONFIG_FIRMLINKS
+ /* For firmlink targets we have to overide what the FS returned for parentid */
+ ||
+ (!is_realdev && vp && (vp->v_flag & VFMLINKTARGET) && vp->v_fmlink &&
+ (alp->commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)))
+#endif
+ )) {
+ // this disables the selectors that were not supported by the filesystem
+ vattr_get_alt_data(vp, alp, vap, return_valid, is_bulk, is_realdev,
+ ctx);
+
+ /* check again */
+ if (!VATTR_ALL_SUPPORTED(vap)) {
+ if (return_valid && pack_invalid) {
+ /* Fix up valid mask for post processing */
+ getattrlist_fixupattrs(&ab.valid, vap, use_fork);
+
+ /* Force packing of everything asked for */
+ vap->va_supported = vap->va_active;
+ } else if (return_valid) {
+ /* Adjust the requested attributes */
+ getattrlist_fixupattrs(
+ (attribute_set_t *)&(alp->commonattr), vap, use_fork);
+ } else {
+ error = EINVAL;
+ }
+ }
+
+ if (error) {
+ goto out;
+ }
+ }
+
+ //if a path is requested, allocate a temporary buffer to build it
+ if (vp && (alp->commonattr & (ATTR_CMN_FULLPATH))) {
+ fullpathptr = (char*) zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO);
+ apaths.fullpathptr = fullpathptr;
+ apaths.fullpathlenp = &fullpathlen;
+ }
+
+ // only interpret fork attributes if they're used as new common attributes
+ if (vp && use_fork) {
+ if (alp->forkattr & (ATTR_CMNEXT_RELPATH)) {
+ relpathptr = (char*) zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO);
+ apaths.relpathptr = relpathptr;
+ apaths.relpathlenp = &relpathlen;
+ }
+
+ if (alp->forkattr & (ATTR_CMNEXT_NOFIRMLINKPATH)) {
+ REALpathptr = (char*) zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO);
+ apaths.REALpathptr = REALpathptr;
+ apaths.REALpathlenp = &REALpathlen;
+ }
+ }
+
+ /*
+ * Compute variable-space requirements.
+ */
+ error = calc_varsize(vp, alp, vap, &varsize, &apaths, &vname, &cnp, &cnl);
+ if (error) {
+ goto out;
+ }
+
+ /*
+ * Allocate a target buffer for attribute results.
+ *
+ * Note that we won't ever copy out more than the caller requested, even though
+ * we might have to allocate more than they offer so that the diagnostic checks
+ * don't result in a panic if the caller's buffer is too small.
+ */
+ ab.allocated = fixedsize + varsize;
+ /* 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;
+ }
+
+ /*
+ * Special handling for bulk calls, align to 8 (and only if enough
+ * space left.
+ */
+ if (is_bulk) {
+ if (buf_size < ab.allocated) {
+ goto out;
+ } else {
+ uint32_t newlen;
+
+ newlen = (ab.allocated + 7) & ~0x07;
+ /* Align only if enough space for alignment */
+ if (newlen <= (uint32_t)buf_size) {
+ ab.allocated = newlen;
+ }
+ }
+ }
+
+ /*
+ * See if we can reuse buffer passed in i.e. it is a kernel buffer
+ * and big enough.
+ */
+ if (uio_isuserspace(auio) || (buf_size < ab.allocated)) {
+ ab.base = kheap_alloc(KHEAP_TEMP, ab.allocated, Z_ZERO | Z_WAITOK);
+ alloc_local_buf = 1;
+ } else {
+ /*
+ * In case this is a kernel buffer and sufficiently
+ * big, this function will try to use that buffer
+ * instead of allocating another buffer and bcopy'ing
+ * into it.
+ *
+ * The calculation below figures out where to start
+ * writing in the buffer and once all the data has been
+ * filled in, uio_resid is updated to reflect the usage
+ * of the buffer.
+ *
+ * uio_offset cannot be used here to determine the
+ * starting location as uio_offset could be set to a
+ * value which has nothing to do the location
+ * in the buffer.
+ */
+ ab.base = (char *)uio_curriovbase(auio) +
+ ((ssize_t)uio_curriovlen(auio) - buf_size);
+ bzero(ab.base, ab.allocated);
+ }
+
+ if (ab.base == NULL) {
+ error = ENOMEM;
+ VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
+ goto out;
+ }
+
+
+ /* set the S_IFMT bits for the mode */
+ if (alp->commonattr & ATTR_CMN_ACCESSMASK) {
+ if (vp) {
+ switch (vp->v_type) {
+ case VREG:
+ vap->va_mode |= S_IFREG;
+ break;
+ case VDIR:
+ vap->va_mode |= S_IFDIR;
+ break;
+ case VBLK:
+ vap->va_mode |= S_IFBLK;
+ break;
+ case VCHR:
+ vap->va_mode |= S_IFCHR;
+ break;
+ case VLNK:
+ vap->va_mode |= S_IFLNK;
+ break;
+ case VSOCK:
+ vap->va_mode |= S_IFSOCK;
+ break;
+ case VFIFO:
+ vap->va_mode |= S_IFIFO;
+ break;
+ default:
+ error = EBADF;
+ goto out;
+ }
+ }
+ }
+
+ /*
+ * 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 ************************************************/
+ error = attr_pack_common(ctx, (options & FSOPT_RETURN_REALDEV ? mp : NULL),
+ vp, alp, &ab, vap, proc_is64, cnp, cnl, fullpathptr, fullpathlen,
+ return_valid, pack_invalid, vtype, is_bulk);
+
+ /* directory attributes *********************************************/
+ if (!error && alp->dirattr && (vtype == VDIR)) {
+ error = attr_pack_dir(vp, alp, &ab, vap, return_valid, pack_invalid);
+ }
+
+ /* file attributes **************************************************/
+ if (!error && alp->fileattr && (vtype != VDIR)) {
+ error = attr_pack_file(ctx, vp, alp, &ab, vap, return_valid,
+ pack_invalid, is_bulk);
+ }
+
+ /* common extended attributes *****************************************/
+ if (!error && use_fork) {
+ error = attr_pack_common_extended(mp, vp, alp, &ab, relpathptr, relpathlen,
+ REALpathptr, REALpathlen, vap, return_valid, pack_invalid);
+ }
+
+ if (error) {
+ goto out;
+ }
+
+ /* diagnostic */
+ if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize) {
+ 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);
+ }
/*
- * Fetch the attribute request.
+ * In the compatible case, we report the smaller of the required and returned sizes.
+ * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
+ * of the result buffer, even if we copied less out. The caller knows how big a buffer
+ * they gave us, so they can always check for truncation themselves.
*/
- if ((error = copyin(uap->alist, &al, sizeof(al))) != 0)
+ *(uint32_t *)ab.base = (options & FSOPT_REPORT_FULLSIZE) ? (uint32_t)ab.needed : (uint32_t)lmin(ab.allocated, ab.needed);
+
+ /* Return attribute set output if requested. */
+ if (return_valid) {
+ 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));
+ }
+
+ copy_size = lmin(buf_size, ab.allocated);
+
+ /* Only actually copyout as much out as the user buffer can hold */
+ if (alloc_local_buf) {
+ error = uiomove(ab.base, (int)copy_size, auio);
+ } else {
+ off_t orig_offset = uio_offset(auio);
+
+ /*
+ * The buffer in the uio struct was used directly
+ * (i.e. it was a kernel buffer and big enough
+ * to hold the data required) in order to avoid
+ * un-needed allocation and copies.
+ *
+ * At this point, update the resid value to what it
+ * would be if this was the result of a uiomove. The
+ * offset is also incremented, though it may not
+ * mean anything to the caller but that is what
+ * uiomove does as well.
+ */
+ uio_setresid(auio, buf_size - copy_size);
+ uio_setoffset(auio, orig_offset + (off_t)copy_size);
+ }
+
+out:
+ if (vname) {
+ vnode_putname(vname);
+ }
+ if (fullpathptr) {
+ zfree(ZV_NAMEI, fullpathptr);
+ }
+ if (relpathptr) {
+ zfree(ZV_NAMEI, relpathptr);
+ }
+ if (REALpathptr) {
+ zfree(ZV_NAMEI, REALpathptr);
+ }
+ if (alloc_local_buf) {
+ kheap_free(KHEAP_TEMP, ab.base, ab.allocated);
+ }
+ return error;
+}
+
+errno_t
+vfs_attr_pack_ext(mount_t mp, vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options,
+ struct vnode_attr *vap, __unused void *fndesc, vfs_context_t ctx)
+{
+ int error;
+ ssize_t fixedsize;
+ uint64_t orig_active;
+ struct attrlist orig_al;
+ enum vtype v_type;
+
+ if (vp) {
+ v_type = vnode_vtype(vp);
+ } else {
+ v_type = vap->va_objtype;
+ }
+
+ orig_al = *alp;
+ orig_active = vap->va_active;
+ vap->va_active = 0;
+
+ error = getattrlist_setupvattr_all(alp, vap, v_type, &fixedsize,
+ proc_is64bit(vfs_context_proc(ctx)), options & FSOPT_ATTR_CMN_EXTENDED);
+
+ if (error) {
+ VFS_DEBUG(ctx, vp,
+ "ATTRLIST - ERROR: setup for request failed");
goto out;
- if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
+ }
+
+ error = vfs_attr_pack_internal(mp, vp, uio, alp,
+ options | FSOPT_REPORT_FULLSIZE, vap, NULL, ctx, 1, v_type,
+ fixedsize);
+
+ VATTR_CLEAR_SUPPORTED_ALL(vap);
+ vap->va_active = orig_active;
+ *alp = orig_al;
+out:
+ return error;
+}
+
+errno_t
+vfs_attr_pack(vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options,
+ struct vnode_attr *vap, __unused void *fndesc, vfs_context_t ctx)
+{
+ return vfs_attr_pack_ext(NULL, vp, uio, alp, options, vap, fndesc, ctx);
+}
+
+/*
+ * Obtain attribute information about a filesystem object.
+ *
+ * Note: The alt_name parameter can be used by the caller to pass in the vnode
+ * name obtained from some authoritative source (eg. readdir vnop); where
+ * filesystems' getattr vnops do not support ATTR_CMN_NAME, the alt_name will be
+ * used as the ATTR_CMN_NAME attribute returned in vnode_attr.va_name.
+ *
+ */
+static int
+getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
+ user_addr_t attributeBuffer, size_t bufferSize, uint64_t options,
+ enum uio_seg segflg, char* authoritative_name, struct ucred *file_cred)
+{
+ struct vnode_attr *va;
+ kauth_action_t action;
+ ssize_t fixedsize;
+ char *va_name;
+ int proc_is64;
+ int error;
+ int return_valid;
+ int pack_invalid;
+ int vtype = 0;
+ uio_t auio;
+ char uio_buf[UIO_SIZEOF(1)];
+ // must be true for fork attributes to be used as new common attributes
+ const int use_fork = (options & FSOPT_ATTR_CMN_EXTENDED) != 0;
+
+ if (bufferSize < sizeof(uint32_t)) {
+ return ERANGE;
+ }
+
+ proc_is64 = proc_is64bit(vfs_context_proc(ctx));
+
+ if (segflg == UIO_USERSPACE) {
+ if (proc_is64) {
+ segflg = UIO_USERSPACE64;
+ } else {
+ segflg = UIO_USERSPACE32;
+ }
+ }
+ auio = uio_createwithbuffer(1, 0, segflg, UIO_READ,
+ &uio_buf[0], sizeof(uio_buf));
+ uio_addiov(auio, attributeBuffer, bufferSize);
+
+ va = kheap_alloc(KHEAP_TEMP, sizeof(struct vnode_attr), Z_WAITOK);
+ VATTR_INIT(va);
+ va_name = NULL;
+
+ if (alp->bitmapcount != ATTR_BIT_MAP_COUNT) {
error = EINVAL;
goto out;
}
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);
+ vp, vfs_context_proc(ctx)->p_comm, alp->commonattr, alp->volattr, alp->fileattr, alp->dirattr, alp->forkattr,
+ (options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
#if CONFIG_MACF
- error = mac_vnode_check_getattrlist(ctx, vp, &al);
- if (error)
+ error = mac_vnode_check_getattrlist(ctx, vp, alp);
+ if (error) {
goto out;
+ }
#endif /* MAC */
/*
- * It is legal to request volume or file attributes,
- * but not both.
+ * It is legal to request volume or file attributes, but not both.
+ *
+ * 26903449 fork attributes can also be requested, but only if they're
+ * interpreted as new, common attributes
*/
- if (al.volattr) {
- if (al.fileattr || al.dirattr || al.forkattr) {
+ if (alp->volattr) {
+ if (alp->fileattr || alp->dirattr || (alp->forkattr && !use_fork)) {
error = EINVAL;
- VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: mixed volume/file/directory/fork attributes");
+ VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: mixed volume/file/directory attributes");
goto out;
}
/* handle volume attribute request */
- error = getvolattrlist(vp, uap, &al, ctx, proc_is64);
+ error = getvolattrlist(ctx, vp, alp, attributeBuffer,
+ bufferSize, options, segflg, proc_is64);
+ goto out;
+ }
+
+ /*
+ * ATTR_CMN_GEN_COUNT and ATTR_CMN_DOCUMENT_ID reuse the bits
+ * originally allocated to ATTR_CMN_NAMEDATTRCOUNT and
+ * ATTR_CMN_NAMEDATTRLIST.
+ */
+ if ((alp->commonattr & (ATTR_CMN_GEN_COUNT | ATTR_CMN_DOCUMENT_ID)) &&
+ !(options & FSOPT_ATTR_CMN_EXTENDED)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /* common extended attributes require FSOPT_ATTR_CMN_EXTENDED option */
+ if (!(use_fork) && (alp->forkattr & ATTR_CMNEXT_VALIDMASK)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /* FSOPT_ATTR_CMN_EXTENDED requires forkattrs are not referenced */
+ if ((options & FSOPT_ATTR_CMN_EXTENDED) && (alp->forkattr & (ATTR_FORK_VALIDMASK))) {
+ error = EINVAL;
goto out;
}
/* Check for special packing semantics */
- return_valid = (al.commonattr & ATTR_CMN_RETURNED_ATTRS);
- pack_invalid = (uap->options & FSOPT_PACK_INVAL_ATTRS);
+ return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) ? 1 : 0;
+ pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS) ? 1 : 0;
if (pack_invalid) {
/* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
- if (!return_valid || al.forkattr) {
+ if (!return_valid || (alp->forkattr && !use_fork)) {
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));
+ bzero(va, sizeof(*va));
}
/* Pick up the vnode type. If the FS is bad and changes vnode types on us, we
*/
vtype = vp->v_type;
-
/*
* Set up the vnode_attr structure and authorise.
*/
- if ((error = getattrlist_setupvattr(&al, &va, &fixedsize, &action, proc_is64, (vtype == VDIR))) != 0) {
+ if ((error = getattrlist_setupvattr(alp, va, &fixedsize, &action, proc_is64, (vtype == VDIR), use_fork)) != 0) {
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
goto out;
}
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) {
+ uint64_t va_active = va->va_active;
- if (va.va_active != 0) {
/*
* If we're going to ask for va_name, allocate a buffer to point it at
*/
- if (VATTR_IS_ACTIVE(&va, va_name)) {
- va.va_name = (char *) kalloc(MAXPATHLEN);
- if (va.va_name == NULL) {
- error = ENOMEM;
- VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: cannot allocate va_name buffer");
- goto out;
+ if (VATTR_IS_ACTIVE(va, va_name)) {
+ va_name = zalloc(ZV_NAMEI);
+ /*
+ * If we have an authoritative_name, prefer that name.
+ *
+ * N.B. Since authoritative_name implies this is coming from getattrlistbulk,
+ * we know the name is authoritative. For /dev/fd, we want to use the file
+ * descriptor as the name not the underlying name of the associate vnode in a
+ * particular file system.
+ */
+ if (authoritative_name) {
+ /* Don't ask the file system */
+ VATTR_CLEAR_ACTIVE(va, va_name);
+ strlcpy(va_name, authoritative_name, MAXPATHLEN);
}
}
+ va->va_name = authoritative_name ? NULL : va_name;
+
+ if (options & FSOPT_RETURN_REALDEV) {
+ va->va_vaflags |= VA_REALFSID;
+ }
+
/*
* Call the filesystem.
*/
- if ((error = vnode_getattr(vp, &va, ctx)) != 0) {
+ if ((error = vnode_getattr(vp, va, ctx)) != 0) {
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
goto out;
}
+#if CONFIG_MACF
+ /*
+ * Give MAC polices a chance to reject or filter the
+ * attributes returned by the filesystem. Note that MAC
+ * policies are consulted *after* calling the filesystem
+ * because filesystems can return more attributes than
+ * were requested so policies wouldn't be authoritative
+ * is consulted beforehand. This also gives policies an
+ * opportunity to change the values of attributes
+ * retrieved.
+ */
+ error = mac_vnode_check_getattr(ctx, file_cred, vp, va);
+ if (error) {
+ VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d", error);
+ goto out;
+ }
+#else
+ (void)file_cred;
+#endif
- /* did we ask for something the filesystem doesn't support? */
- if (!VATTR_ALL_SUPPORTED(&va)) {
-
- /*
- * There are a couple of special cases. If we are after object IDs,
- * we can make do with va_fileid.
- */
- 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 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)) {
- 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);
- }
+ /*
+ * It we ask for the name, i.e., vname is non null and
+ * we have an authoritative name, then reset va_name is
+ * active and if needed set va_name is supported.
+ *
+ * A (buggy) filesystem may change fields which belong
+ * to us. We try to deal with that here as well.
+ */
+ va->va_active = va_active;
+ if (authoritative_name && va_name) {
+ VATTR_SET_ACTIVE(va, va_name);
+ if (!(VATTR_IS_SUPPORTED(va, va_name))) {
+ VATTR_SET_SUPPORTED(va, va_name);
}
- /*
- * And we can report datasize/alloc from total.
- */
- if ((al.fileattr & ATTR_FILE_DATALENGTH) && !VATTR_IS_SUPPORTED(&va, va_data_size))
- VATTR_CLEAR_ACTIVE(&va, va_data_size);
- if ((al.fileattr & ATTR_FILE_DATAALLOCSIZE) && !VATTR_IS_SUPPORTED(&va, va_data_alloc))
- VATTR_CLEAR_ACTIVE(&va, va_data_alloc);
+ }
+ va->va_name = va_name;
+ }
- /*
- * If we don't have an encoding, go with UTF-8
- */
- if ((al.commonattr & ATTR_CMN_SCRIPT) &&
- !VATTR_IS_SUPPORTED(&va, va_encoding) && !return_valid)
- VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
+ error = vfs_attr_pack_internal(vp->v_mount, vp, auio, alp, options, va, NULL, ctx,
+ 0, vtype, fixedsize);
- /*
- * If we don't have a name, we'll get one from the vnode or mount point.
- */
- if ((al.commonattr & ATTR_CMN_NAME) && !VATTR_IS_SUPPORTED(&va, va_name)) {
- VATTR_CLEAR_ACTIVE(&va, va_name);
- }
+out:
+ if (va_name) {
+ zfree(ZV_NAMEI, va_name);
+ }
+ if (VATTR_IS_SUPPORTED(va, va_acl) && (va->va_acl != NULL)) {
+ kauth_acl_free(va->va_acl);
+ }
+ kheap_free(KHEAP_TEMP, va, sizeof(struct vnode_attr));
- /* If va_dirlinkcount isn't supported use a default of 1. */
- if ((al.dirattr & ATTR_DIR_LINKCOUNT) && !VATTR_IS_SUPPORTED(&va, va_dirlinkcount)) {
- VATTR_RETURN(&va, va_dirlinkcount, 1);
- }
-
- /* check again */
- if (!VATTR_ALL_SUPPORTED(&va)) {
- 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;
- }
- }
- }
+ VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
+ return error;
+}
+
+int
+fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval)
+{
+ vfs_context_t ctx;
+ vnode_t vp;
+ int error;
+ struct attrlist al;
+ struct fileproc *fp;
+
+ ctx = vfs_context_current();
+ vp = NULL;
+ fp = NULL;
+ error = 0;
+
+ if ((error = fp_get_ftype(p, uap->fd, DTYPE_VNODE, EINVAL, &fp)) != 0) {
+ return error;
+ }
+ vp = (struct vnode *)fp->fp_glob->fg_data;
+
+ if ((error = vnode_getwithref(vp)) != 0) {
+ goto out;
}
/*
- * Compute variable-space requirements.
+ * Fetch the attribute request.
*/
- 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);
- } else {
- if (vnode_isvroot(vp)) {
- if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
- 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
- * pre Tiger code. returning nothing for the boot volume name
- * breaks installers - 3961058
- */
- cnp = vname = vnode_getname(vp);
- if (cnp == NULL) {
- /* just use "/" as name */
- cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
- }
- cnl = strlen(cnp);
- }
- else {
- getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
- }
- } else {
- cnp = vname = vnode_getname(vp);
- cnl = 0;
- if (cnp != NULL) {
- cnl = strlen(cnp);
- }
- }
- }
- varsize += roundup(cnl + 1, 4);
+ error = copyin(uap->alist, &al, sizeof(al));
+ if (error) {
+ goto out_vnode_put;
}
- /*
- * 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.
+ /* Default to using the vnode's name. */
+ error = getattrlist_internal(ctx, vp, &al, uap->attributeBuffer,
+ uap->bufferSize, uap->options,
+ (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : \
+ UIO_USERSPACE32), NULL,
+ fp->fp_glob->fg_cred);
+
+out_vnode_put:
+ vnode_put(vp);
+out:
+ fp_drop(p, uap->fd, fp, 0);
+
+ return error;
+}
+
+static int
+getattrlistat_internal(vfs_context_t ctx, user_addr_t path,
+ struct attrlist *alp, user_addr_t attributeBuffer, size_t bufferSize,
+ uint64_t options, enum uio_seg segflg, enum uio_seg pathsegflg, int fd)
+{
+ struct nameidata nd;
+ vnode_t vp;
+ int32_t nameiflags;
+ int error;
+
+ nameiflags = 0;
+ /*
+ * Look up the file.
*/
- 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);
+ if (!(options & FSOPT_NOFOLLOW)) {
+ nameiflags |= FOLLOW;
+ }
+
+ nameiflags |= AUDITVNPATH1;
+ NDINIT(&nd, LOOKUP, OP_GETATTR, nameiflags, pathsegflg,
+ path, ctx);
+
+ error = nameiat(&nd, fd);
+
+ if (error) {
+ return error;
}
+ vp = nd.ni_vp;
+
+ error = getattrlist_internal(ctx, vp, alp, attributeBuffer,
+ bufferSize, options, segflg, NULL, NOCRED);
+
+ /* Retain the namei reference until the getattrlist completes. */
+ nameidone(&nd);
+ vnode_put(vp);
+ return error;
+}
+
+int
+getattrlist(proc_t p, struct getattrlist_args *uap, __unused int32_t *retval)
+{
+ enum uio_seg segflg;
+ struct attrlist al;
+ int error;
+
+ segflg = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32;
+
/*
- * We have a kauth_acl_t but we will be returning a kauth_filesec_t.
- *
- * XXX This needs to change at some point; since the blob is opaque in
- * user-space this is OK.
+ * Fetch the attribute request.
*/
- 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);
-
+ error = copyin(uap->alist, &al, sizeof(al));
+ if (error) {
+ return error;
+ }
+
+ return getattrlistat_internal(vfs_context_current(),
+ CAST_USER_ADDR_T(uap->path), &al,
+ CAST_USER_ADDR_T(uap->attributeBuffer), uap->bufferSize,
+ (uint64_t)uap->options, segflg, segflg, AT_FDCWD);
+}
+
+int
+getattrlistat(proc_t p, struct getattrlistat_args *uap, __unused int32_t *retval)
+{
+ enum uio_seg segflg;
+ struct attrlist al;
+ int error;
+
+ segflg = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32;
+
/*
- * Allocate a target buffer for attribute results.
- *
- * Note that we won't ever copy out more than the caller requested, even though
- * we might have to allocate more than they offer so that the diagnostic checks
- * don't result in a panic if the caller's buffer is too small..
+ * Fetch the attribute request.
*/
- ab.allocated = 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);
- goto out;
- }
- MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_WAITOK);
- if (ab.base == NULL) {
- error = ENOMEM;
- VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
- goto out;
+ error = copyin(uap->alist, &al, sizeof(al));
+ if (error) {
+ return error;
}
- /* set the S_IFMT bits for the mode */
- if (al.commonattr & ATTR_CMN_ACCESSMASK) {
- switch (vp->v_type) {
- case VREG:
- va.va_mode |= S_IFREG;
- break;
- case VDIR:
- va.va_mode |= S_IFDIR;
- break;
- case VBLK:
- va.va_mode |= S_IFBLK;
- break;
- case VCHR:
- va.va_mode |= S_IFCHR;
- break;
- case VLNK:
- va.va_mode |= S_IFLNK;
- break;
- case VSOCK:
- va.va_mode |= S_IFSOCK;
- break;
- case VFIFO:
- va.va_mode |= S_IFIFO;
- break;
- default:
- error = EBADF;
- goto out;
+ return getattrlistat_internal(vfs_context_current(),
+ CAST_USER_ADDR_T(uap->path), &al,
+ CAST_USER_ADDR_T(uap->attributeBuffer), uap->bufferSize,
+ (uint64_t)uap->options, segflg, segflg, uap->fd);
+}
+
+/*
+ * This refills the per-fd direntries cache by issuing a VNOP_READDIR.
+ * It attempts to try and find a size the filesystem responds to, so
+ * it first tries 1 direntry sized buffer and going from 1 to 2 to 4
+ * direntry sized buffers to readdir. If the filesystem does not respond
+ * to 4 * direntry it returns the error by the filesystem (if any) and sets
+ * EOF.
+ *
+ * This function also tries again if the last "refill" returned an EOF
+ * to try and get any additional entries if they were added after the last
+ * refill.
+ */
+static int
+refill_fd_direntries(vfs_context_t ctx, vnode_t dvp, struct fd_vn_data *fvd,
+ int *eofflagp)
+{
+ uio_t rdir_uio;
+ char uio_buf[UIO_SIZEOF(1)];
+ size_t rdirbufsiz;
+ size_t rdirbufused;
+ int eofflag;
+ int nentries;
+ int error;
+
+ /*
+ * If the last readdir returned EOF, don't try again.
+ */
+ if (fvd->fv_eofflag) {
+ *eofflagp = 1;
+ if (fvd->fv_buf) {
+ kheap_free(KHEAP_DATA_BUFFERS, fvd->fv_buf, fvd->fv_bufallocsiz);
+ fvd->fv_buf = NULL;
}
+ return 0;
}
+ error = 0;
+
/*
- * Pack results into the destination buffer.
+ * If there is a cached allocation size of the dirbuf that should be
+ * allocated, use that. Otherwise start with a allocation size of
+ * FV_DIRBUF_START_SIZ. This start size may need to be increased if the
+ * filesystem doesn't respond to the initial size.
*/
- 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 **************************************************/
- if (al.commonattr & ATTR_CMN_NAME) {
- attrlist_pack_string(&ab, cnp, cnl);
- ab.actual.commonattr |= ATTR_CMN_NAME;
- }
- if (al.commonattr & ATTR_CMN_DEVID) {
- ATTR_PACK4(ab, vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
- ab.actual.commonattr |= ATTR_CMN_DEVID;
- }
- if (al.commonattr & ATTR_CMN_FSID) {
- ATTR_PACK8(ab, vp->v_mount->mnt_vfsstat.f_fsid);
- ab.actual.commonattr |= ATTR_CMN_FSID;
- }
- if (al.commonattr & ATTR_CMN_OBJTYPE) {
- ATTR_PACK4(ab, vtype);
- ab.actual.commonattr |= ATTR_CMN_OBJTYPE;
+ if (fvd->fv_offset && fvd->fv_bufallocsiz) {
+ rdirbufsiz = fvd->fv_bufallocsiz;
+ } else {
+ rdirbufsiz = FV_DIRBUF_START_SIZ;
}
- if (al.commonattr & ATTR_CMN_OBJTAG) {
- ATTR_PACK4(ab, vp->v_tag);
- ab.actual.commonattr |= ATTR_CMN_OBJTAG;
+
+ *eofflagp = 0;
+
+ rdir_uio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
+ &uio_buf[0], sizeof(uio_buf));
+
+retry_alloc:
+ /*
+ * Don't explicitly zero out this buffer since this is
+ * not copied out to user space.
+ */
+ if (!fvd->fv_buf) {
+ fvd->fv_buf = kheap_alloc(KHEAP_DATA_BUFFERS, rdirbufsiz, Z_WAITOK);
+ fvd->fv_bufallocsiz = rdirbufsiz;
+ fvd->fv_bufdone = 0;
}
- if (al.commonattr & ATTR_CMN_OBJID) {
- fsobj_id_t f;
+
+ uio_reset(rdir_uio, fvd->fv_eoff, UIO_SYSSPACE, UIO_READ);
+ uio_addiov(rdir_uio, CAST_USER_ADDR_T(fvd->fv_buf), rdirbufsiz);
+
+ /*
+ * Some filesystems do not set nentries or eofflag...
+ */
+ eofflag = 0;
+ nentries = 0;
+ error = vnode_readdir64(dvp, rdir_uio, VNODE_READDIR_EXTENDED,
+ &eofflag, &nentries, ctx);
+
+ rdirbufused = rdirbufsiz - (size_t)uio_resid(rdir_uio);
+
+ if (!error && (rdirbufused > 0) && (rdirbufused <= rdirbufsiz)) {
+ /* Save offsets */
+ fvd->fv_soff = fvd->fv_eoff;
+ fvd->fv_eoff = uio_offset(rdir_uio);
+ /* Save eofflag state but don't return EOF for this time.*/
+ fvd->fv_eofflag = eofflag;
+ eofflag = 0;
+ /* Reset buffer parameters */
+ fvd->fv_bufsiz = rdirbufused;
+ fvd->fv_bufdone = 0;
+ bzero(fvd->fv_buf + rdirbufused, rdirbufsiz - rdirbufused);
+ } else if (!eofflag && (rdirbufsiz < FV_DIRBUF_MAX_SIZ)) {
/*
- * Carbon can't deal with us reporting the target ID
- * for links. So we ask the filesystem to give us the
- * source ID as well, and if it gives us one, we use
- * it instead.
+ * Some Filesystems have higher requirements for the
+ * smallest buffer size they will respond to for a
+ * directory listing. Start (relatively) small but increase
+ * it upto FV_DIRBUF_MAX_SIZ. Most should be good with
+ * 1*direntry. Cache the size found so that this does not need
+ * need to be done every time. This also means that an error
+ * from VNOP_READDIR is ignored until at least FV_DIRBUF_MAX_SIZ
+ * has been attempted.
*/
- if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
- f.fid_objno = va.va_linkid;
- } else {
- f.fid_objno = va.va_fileid;
- }
- f.fid_generation = 0;
- ATTR_PACK8(ab, f);
- ab.actual.commonattr |= ATTR_CMN_OBJID;
- }
- if (al.commonattr & ATTR_CMN_OBJPERMANENTID) {
- fsobj_id_t f;
+ kheap_free(KHEAP_DATA_BUFFERS, fvd->fv_buf, fvd->fv_bufallocsiz);
+ rdirbufsiz = 2 * rdirbufsiz;
+ fvd->fv_bufallocsiz = 0;
+ goto retry_alloc;
+ } else if (!error) {
/*
- * Carbon can't deal with us reporting the target ID
- * for links. So we ask the filesystem to give us the
- * source ID as well, and if it gives us one, we use
- * it instead.
+ * The Filesystem did not set eofflag but also did not
+ * return any entries (or an error). It is presumed that
+ * EOF has been reached.
*/
- if (VATTR_IS_SUPPORTED(&va, va_linkid)) {
- f.fid_objno = va.va_linkid;
- } else {
- f.fid_objno = va.va_fileid;
- }
- f.fid_generation = 0;
- ATTR_PACK8(ab, f);
- ab.actual.commonattr |= ATTR_CMN_OBJPERMANENTID;
+ fvd->fv_eofflag = eofflag = 1;
}
- if (al.commonattr & ATTR_CMN_PAROBJID) {
- fsobj_id_t f;
- f.fid_objno = va.va_parentid; /* could be lossy here! */
- f.fid_generation = 0;
- ATTR_PACK8(ab, f);
- ab.actual.commonattr |= ATTR_CMN_PAROBJID;
- }
- 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);
- ab.actual.commonattr |= ATTR_CMN_CRTIME;
- }
- if (al.commonattr & ATTR_CMN_MODTIME) {
- ATTR_PACK_TIME(ab, va.va_modify_time, proc_is64);
- ab.actual.commonattr |= ATTR_CMN_MODTIME;
- }
- if (al.commonattr & ATTR_CMN_CHGTIME) {
- ATTR_PACK_TIME(ab, va.va_change_time, proc_is64);
- ab.actual.commonattr |= ATTR_CMN_CHGTIME;
- }
- if (al.commonattr & ATTR_CMN_ACCTIME) {
- ATTR_PACK_TIME(ab, va.va_access_time, proc_is64);
- ab.actual.commonattr |= ATTR_CMN_ACCTIME;
- }
- if (al.commonattr & ATTR_CMN_BKUPTIME) {
- ATTR_PACK_TIME(ab, va.va_backup_time, proc_is64);
- ab.actual.commonattr |= ATTR_CMN_BKUPTIME;
+ /*
+ * If the filesystem returned an error and it had previously returned
+ * EOF, ignore the error and set EOF.
+ */
+ if (error && fvd->fv_eofflag) {
+ eofflag = 1;
+ error = 0;
}
+
/*
- * 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 either the directory has either hit EOF or an error, now is a good
+ * time to free up directory entry buffer.
*/
- if (al.commonattr & ATTR_CMN_USERACCESS) { /* this is expensive */
- if (vtype == VDIR) {
- if (vnode_authorize(vp, NULL,
- KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
- perms |= W_OK;
- if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
- perms |= R_OK;
- if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_SEARCH, ctx) == 0)
- perms |= X_OK;
- } else {
- if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_WRITE_DATA, ctx) == 0)
- perms |= W_OK;
- if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
- perms |= R_OK;
- if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
- perms |= X_OK;
+ if ((error || eofflag) && fvd->fv_buf) {
+ kheap_free(KHEAP_DATA_BUFFERS, fvd->fv_buf, fvd->fv_bufallocsiz);
+ if (error) {
+ fvd->fv_bufallocsiz = 0;
}
}
-
- 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;
+ *eofflagp = eofflag;
+
+ return error;
+}
+
+/*
+ * gets the current direntry. To advance to the next direntry this has to be
+ * paired with a direntry_done.
+ *
+ * Since directories have restrictions on where directory enumeration
+ * can restart from, entries are first read into* a per fd diectory entry
+ * "cache" and entries provided from that cache.
+ */
+static int
+get_direntry(vfs_context_t ctx, vnode_t dvp, struct fd_vn_data *fvd,
+ int *eofflagp, struct direntry **dpp)
+{
+ int eofflag;
+ int error;
+
+ *eofflagp = 0;
+ *dpp = NULL;
+ error = 0;
+ if (!fvd->fv_bufsiz) {
+ error = refill_fd_direntries(ctx, dvp, fvd, &eofflag);
+ if (error) {
+ return error;
}
- 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 (eofflag) {
+ *eofflagp = eofflag;
+ return error;
}
}
- 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
- * the smallest set of permissions granted by both MAC & DAC
- * checks. We won't add back any permissions.
- */
- if (perms & W_OK)
- if (mac_vnode_check_access(ctx, vp, W_OK) != 0)
- perms &= ~W_OK;
- if (perms & R_OK)
- if (mac_vnode_check_access(ctx, vp, R_OK) != 0)
- perms &= ~R_OK;
- if (perms & X_OK)
- if (mac_vnode_check_access(ctx, vp, X_OK) != 0)
- perms &= ~X_OK;
-#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)) {
- struct kauth_filesec fsec;
- /*
- * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
- */
- fsec.fsec_magic = KAUTH_FILESEC_MAGIC;
- fsec.fsec_owner = kauth_null_guid;
- fsec.fsec_group = kauth_null_guid;
- 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);
+
+ *dpp = (struct direntry *)(fvd->fv_buf + fvd->fv_bufdone);
+ return error;
+}
+
+/*
+ * Advances to the next direntry.
+ */
+static void
+direntry_done(struct fd_vn_data *fvd)
+{
+ struct direntry *dp;
+
+ dp = (struct direntry *)(fvd->fv_buf + fvd->fv_bufdone);
+ if (dp->d_reclen) {
+ fvd->fv_bufdone += dp->d_reclen;
+ if (fvd->fv_bufdone > fvd->fv_bufsiz) {
+ fvd->fv_bufdone = fvd->fv_bufsiz;
}
+ } else {
+ fvd->fv_bufdone = fvd->fv_bufsiz;
}
- if (al.commonattr & ATTR_CMN_UUID) {
- 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 we're at the end the fd direntries cache, reset the
+ * cache trackers.
+ */
+ if (fvd->fv_bufdone == fvd->fv_bufsiz) {
+ fvd->fv_bufdone = 0;
+ fvd->fv_bufsiz = 0;
}
- 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);
+}
+
+/*
+ * A stripped down version of getattrlist_internal to fill in only select
+ * attributes in case of an error from getattrlist_internal.
+ *
+ * It always returns at least ATTR_BULK_REQUIRED i.e. the name (but may also
+ * return some other attributes which can be obtained from the vnode).
+ *
+ * It does not change the value of the passed in attrlist.
+ *
+ * The objective of this function is to fill in an "error entry", i.e.
+ * an entry with ATTR_CMN_RETURNED_ATTRS & ATTR_CMN_NAME. If the caller
+ * has also asked for ATTR_CMN_ERROR, it is filled in as well.
+ *
+ * Input
+ * vp - vnode pointer
+ * alp - pointer to attrlist struct.
+ * options - options passed to getattrlistbulk(2)
+ * kern_attr_buf - Kernel buffer to fill data (assumes offset 0 in
+ * buffer)
+ * kern_attr_buf_siz - Size of buffer.
+ * needs_error_attr - Whether the caller asked for ATTR_CMN_ERROR
+ * error_attr - This value is used to fill ATTR_CMN_ERROR (if the user
+ * has requested it in the attribute list.
+ * namebuf - This is used to fill in the name.
+ * ctx - vfs context of caller.
+ */
+static void
+get_error_attributes(vnode_t vp, struct attrlist *alp, uint64_t options,
+ user_addr_t kern_attr_buf, size_t kern_attr_buf_siz, int error_attr,
+ caddr_t namebuf, vfs_context_t ctx)
+{
+ size_t fsiz, vsiz;
+ struct _attrlist_buf ab;
+ size_t namelen;
+ kauth_action_t action;
+ struct attrlist al;
+ int needs_error_attr = (alp->commonattr & ATTR_CMN_ERROR);
+
+ /*
+ * To calculate fixed size required, in the FSOPT_PACK_INVAL_ATTRS case,
+ * the fixedsize should include space for all the attributes asked by
+ * the user. Only ATTR_BULK_REQUIRED (and ATTR_CMN_ERROR) will be filled
+ * and will be valid. All other attributes are zeroed out later.
+ *
+ * ATTR_CMN_RETURNED_ATTRS, ATTR_CMN_ERROR and ATTR_CMN_NAME
+ * (the only valid ones being returned from here) happen to be
+ * the first three attributes by order as well.
+ */
+ al = *alp;
+ if (!(options & FSOPT_PACK_INVAL_ATTRS)) {
+ /*
+ * In this case the fixedsize only needs to be only for the
+ * attributes being actually returned.
+ */
+ al.commonattr = ATTR_BULK_REQUIRED;
+ if (needs_error_attr) {
+ al.commonattr |= ATTR_CMN_ERROR;
}
+ al.fileattr = 0;
+ al.dirattr = 0;
}
- if (al.commonattr & ATTR_CMN_FILEID) {
- ATTR_PACK8(ab, va.va_fileid);
- ab.actual.commonattr |= ATTR_CMN_FILEID;
- }
- if (al.commonattr & ATTR_CMN_PARENTID) {
- ATTR_PACK8(ab, va.va_parentid);
- ab.actual.commonattr |= ATTR_CMN_PARENTID;
+
+ /*
+ * Passing NULL for the vnode_attr pointer is valid for
+ * getattrlist_setupvattr. All that is required is the size.
+ */
+ fsiz = 0;
+ (void)getattrlist_setupvattr(&al, NULL, (ssize_t *)&fsiz,
+ &action, proc_is64bit(vfs_context_proc(ctx)),
+ (vnode_vtype(vp) == VDIR), (options & FSOPT_ATTR_CMN_EXTENDED));
+
+ namelen = strlen(namebuf);
+ vsiz = namelen + 1;
+ vsiz = ((vsiz + 3) & ~0x03);
+
+ bzero(&ab, sizeof(ab));
+ ab.base = (char *)kern_attr_buf;
+ ab.needed = fsiz + vsiz;
+
+ /* Fill in the size needed */
+ *((uint32_t *)ab.base) = (uint32_t)ab.needed;
+ if (ab.needed > (ssize_t)kern_attr_buf_siz) {
+ goto out;
}
-
- if (al.commonattr & ATTR_CMN_FULLPATH) {
- attrlist_pack_string (&ab, fullpathptr, fullpathlen);
- ab.actual.commonattr |= ATTR_CMN_FULLPATH;
+
+ /*
+ * Setup to pack results into the destination buffer.
+ */
+ ab.fixedcursor = ab.base + sizeof(uint32_t);
+ /*
+ * Zero out buffer, ab.fixedbuffer starts after the first uint32_t
+ * which gives the length. This ensures everything that we don't
+ * fill in explicitly later is zeroed out correctly.
+ */
+ bzero(ab.fixedcursor, fsiz);
+ /*
+ * variable size data should start after all the fixed
+ * size data.
+ */
+ ab.varcursor = ab.base + fsiz;
+ /*
+ * Initialise the value for ATTR_CMN_RETURNED_ATTRS and leave space
+ * Leave space for filling in its value here at the end.
+ */
+ bzero(&ab.actual, sizeof(ab.actual));
+ ab.fixedcursor += sizeof(attribute_set_t);
+
+ ab.allocated = ab.needed;
+
+ /* Fill ATTR_CMN_ERROR (if asked for) */
+ if (needs_error_attr) {
+ ATTR_PACK4(ab, error_attr);
+ ab.actual.commonattr |= ATTR_CMN_ERROR;
}
-
- if (al.commonattr & ATTR_CMN_ADDEDTIME) {
- ATTR_PACK_TIME(ab, va.va_addedtime, proc_is64);
- ab.actual.commonattr |= ATTR_CMN_ADDEDTIME;
+
+ /*
+ * Fill ATTR_CMN_NAME, The attrrefrence is packed at this location
+ * but the actual string itself is packed after fixedsize which set
+ * to different lengths based on whether FSOPT_PACK_INVAL_ATTRS
+ * was passed.
+ */
+ attrlist_pack_string(&ab, namebuf, namelen);
+
+ /*
+ * Now Fill in ATTR_CMN_RETURNED_ATTR. This copies to a
+ * location after the count i.e. before ATTR_CMN_ERROR and
+ * ATTR_CMN_NAME.
+ */
+ ab.actual.commonattr |= ATTR_CMN_NAME | ATTR_CMN_RETURNED_ATTRS;
+ bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof(ab.actual));
+out:
+ return;
+}
+
+/*
+ * This is the buffer size required to return at least 1 entry. We need space
+ * for the length, for ATTR_CMN_RETURNED_ATTRS and ATTR_CMN_NAME. Assuming the
+ * smallest filename of a single byte we get
+ */
+
+#define MIN_BUF_SIZE_REQUIRED (sizeof(uint32_t) + sizeof(attribute_set_t) +\
+ sizeof(attrreference_t))
+
+/*
+ * Read directory entries and get attributes filled in for each directory
+ */
+static int
+readdirattr(vnode_t dvp, struct fd_vn_data *fvd, uio_t auio,
+ struct attrlist *alp, uint64_t options, int *count, int *eofflagp,
+ vfs_context_t ctx)
+{
+ caddr_t kern_attr_buf;
+ size_t kern_attr_buf_siz;
+ caddr_t max_path_name_buf = NULL;
+ int error = 0;
+
+ *count = 0;
+ *eofflagp = 0;
+
+ if (uio_iovcnt(auio) > 1) {
+ return EINVAL;
}
+ /*
+ * We fill in a kernel buffer for the attributes and uiomove each
+ * entry's attributes (as returned by getattrlist_internal)
+ */
+ kern_attr_buf_siz = uio_resid(auio);
+ if (kern_attr_buf_siz > ATTR_MAX_BUFFER) {
+ kern_attr_buf_siz = ATTR_MAX_BUFFER;
+ } else if (kern_attr_buf_siz == 0) {
+ /* Nothing to do */
+ return error;
+ }
+
+ kern_attr_buf = kheap_alloc(KHEAP_TEMP, kern_attr_buf_siz, Z_WAITOK);
+
+ while (uio_resid(auio) > (user_ssize_t)MIN_BUF_SIZE_REQUIRED) {
+ struct direntry *dp;
+ user_addr_t name_buffer;
+ struct nameidata nd;
+ vnode_t vp;
+ struct attrlist al;
+ size_t entlen;
+ size_t bytes_left;
+ size_t pad_bytes;
+ ssize_t new_resid;
- /* 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);
- ab.actual.dirattr |= ATTR_DIR_LINKCOUNT;
+ /*
+ * get_direntry returns the current direntry and does not
+ * advance. A move to the next direntry only happens if
+ * direntry_done is called.
+ */
+ error = get_direntry(ctx, dvp, fvd, eofflagp, &dp);
+ if (error || (*eofflagp) || !dp) {
+ break;
}
- if (al.dirattr & ATTR_DIR_ENTRYCOUNT) {
- ATTR_PACK4(ab, (uint32_t)va.va_nchildren);
- ab.actual.dirattr |= ATTR_DIR_ENTRYCOUNT;
+
+ /*
+ * skip "." and ".." (and a bunch of other invalid conditions.)
+ */
+ if (!dp->d_reclen || dp->d_ino == 0 || dp->d_namlen == 0 ||
+ (dp->d_namlen == 1 && dp->d_name[0] == '.') ||
+ (dp->d_namlen == 2 && dp->d_name[0] == '.' &&
+ dp->d_name[1] == '.')) {
+ direntry_done(fvd);
+ continue;
}
- 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;
+ /*
+ * try to deal with not-null terminated filenames.
+ */
+ if (dp->d_name[dp->d_namlen] != '\0') {
+ if (!max_path_name_buf) {
+ max_path_name_buf = zalloc_flags(ZV_NAMEI, Z_WAITOK);
}
-#endif
- ATTR_PACK4(ab, mntstat);
- ab.actual.dirattr |= ATTR_DIR_MOUNTSTATUS;
+ bcopy(dp->d_name, max_path_name_buf, dp->d_namlen);
+ max_path_name_buf[dp->d_namlen] = '\0';
+ name_buffer = CAST_USER_ADDR_T(max_path_name_buf);
+ } else {
+ name_buffer = CAST_USER_ADDR_T(&(dp->d_name));
}
- }
- /* file attributes **************************************************/
- 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
+ /*
+ * We have an iocount on the directory already.
+ *
+ * Note that we supply NOCROSSMOUNT to the namei call as we attempt to acquire
+ * a vnode for this particular entry. This is because the native call will
+ * (likely) attempt to emit attributes based on its own metadata in order to avoid
+ * creating vnodes where posssible. If the native call is not going to walk
+ * up the vnode mounted-on chain in order to find the top-most mount point, then we
+ * should not either in this emulated readdir+getattrlist() approach. We
+ * will be responsible for setting DIR_MNTSTATUS_MNTPOINT on that directory that
+ * contains a mount point.
*/
- 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;
+ NDINIT(&nd, LOOKUP, OP_GETATTR, (AUDITVNPATH1 | USEDVP | NOCROSSMOUNT),
+ UIO_SYSSPACE, CAST_USER_ADDR_T(name_buffer), ctx);
- 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);
- }
- }
+ nd.ni_dvp = dvp;
+ error = namei(&nd);
- if (al.fileattr & ATTR_FILE_LINKCOUNT) {
- ATTR_PACK4(ab, (uint32_t)va.va_nlink);
- ab.actual.fileattr |= ATTR_FILE_LINKCOUNT;
+ if (error) {
+ direntry_done(fvd);
+ error = 0;
+ continue;
}
+
+ vp = nd.ni_vp;
+
/*
- * 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).
+ * getattrlist_internal can change the values of the
+ * the required attribute list. Copy the current values
+ * and use that one instead.
*/
- if (al.fileattr & ATTR_FILE_TOTALSIZE) {
- uint64_t totalsize = rlength;
+ al = *alp;
- if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
- totalsize += va.va_data_size;
- } else {
- totalsize += va.va_total_size;
- }
+ error = getattrlist_internal(ctx, vp, &al,
+ CAST_USER_ADDR_T(kern_attr_buf), kern_attr_buf_siz,
+ options | FSOPT_REPORT_FULLSIZE, UIO_SYSSPACE,
+ CAST_DOWN_EXPLICIT(char *, name_buffer),
+ NOCRED);
- 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);
- 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) {
- uint32_t dev;
+ nameidone(&nd);
- if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
- if (vp->v_specinfo != NULL)
- dev = vp->v_specinfo->si_rdev;
- else
- dev = va.va_rdev;
- } else {
- dev = 0;
- }
- ATTR_PACK4(ab, dev);
- ab.actual.fileattr |= ATTR_FILE_DEVTYPE;
+ if (error) {
+ get_error_attributes(vp, alp, options,
+ CAST_USER_ADDR_T(kern_attr_buf),
+ kern_attr_buf_siz, error, (caddr_t)name_buffer,
+ ctx);
+ error = 0;
}
-
- /*
- * If the filesystem does not support datalength
- * or dataallocsize, then we infer that totalsize and
- * totalalloc are substitutes.
+
+ /* Done with vnode now */
+ vnode_put(vp);
+
+ /*
+ * Because FSOPT_REPORT_FULLSIZE was set, the first 4 bytes
+ * of the buffer returned by getattrlist contains the size
+ * (even if the provided buffer isn't sufficiently big). Use
+ * that to check if we've run out of buffer space.
+ *
+ * resid is a signed type, and the size of the buffer etc
+ * are unsigned types. It is theoretically possible for
+ * resid to be < 0 and in which case we would be assigning
+ * an out of bounds value to bytes_left (which is unsigned)
+ * uiomove takes care to not ever set resid to < 0, so it
+ * is safe to do this here.
*/
- 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;
+ bytes_left = (size_t)((user_size_t)uio_resid(auio));
+ entlen = (size_t)(*((uint32_t *)(kern_attr_buf)));
+ if (!entlen || (entlen > bytes_left)) {
+ break;
}
- if (al.fileattr & ATTR_FILE_DATAALLOCSIZE) {
- if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
- ATTR_PACK8(ab, va.va_data_alloc);
+
+ /*
+ * Will the pad bytes fit as well ? If they can't be, still use
+ * this entry but this will be the last entry returned.
+ */
+ pad_bytes = ((entlen + 7) & ~0x07) - entlen;
+ new_resid = 0;
+ if (pad_bytes && (entlen + pad_bytes <= bytes_left)) {
+ /*
+ * While entlen can never be > ATTR_MAX_BUFFER,
+ * (entlen + pad_bytes) can be, handle that and
+ * zero out the pad bytes. N.B. - Only zero
+ * out information in the kernel buffer that is
+ * going to be uiomove'ed out.
+ */
+ if (entlen + pad_bytes <= kern_attr_buf_siz) {
+ /* This is the normal case. */
+ bzero(kern_attr_buf + entlen, pad_bytes);
} else {
- ATTR_PACK8(ab, va.va_total_alloc);
+ bzero(kern_attr_buf + entlen,
+ kern_attr_buf_siz - entlen);
+ /*
+ * Pad bytes left over, change the resid value
+ * manually. We only got in here because
+ * bytes_left >= entlen + pad_bytes so
+ * new_resid (which is a signed type) is
+ * always positive.
+ */
+ new_resid = (ssize_t)(bytes_left -
+ (entlen + pad_bytes));
}
- ab.actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
+ entlen += pad_bytes;
}
- /* already got the resource fork size/allocation above */
- if (al.fileattr & ATTR_FILE_RSRCLENGTH) {
- ATTR_PACK8(ab, rlength);
- ab.actual.fileattr |= ATTR_FILE_RSRCLENGTH;
+ *((uint32_t *)kern_attr_buf) = (uint32_t)entlen;
+ error = uiomove(kern_attr_buf, min((int)entlen, (int)kern_attr_buf_siz),
+ auio);
+
+ if (error) {
+ break;
}
- if (al.fileattr & ATTR_FILE_RSRCALLOCSIZE) {
- ATTR_PACK8(ab, ralloc);
- ab.actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE;
+
+ if (new_resid) {
+ uio_setresid(auio, (user_ssize_t)new_resid);
}
+
+ /*
+ * At this point, the directory entry has been consumed, proceed
+ * to the next one.
+ */
+ (*count)++;
+ direntry_done(fvd);
}
- /* diagnostic */
- 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);
+ if (max_path_name_buf) {
+ zfree(ZV_NAMEI, max_path_name_buf);
+ }
/*
- * In the compatible case, we report the smaller of the required and returned sizes.
- * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
- * of the result buffer, even if we copied less out. The caller knows how big a buffer
- * they gave us, so they can always check for truncation themselves.
+ * At this point, kern_attr_buf is always allocated
*/
- *(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
+ kheap_free(KHEAP_TEMP, kern_attr_buf, kern_attr_buf_siz);
- /* 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));
-
-out:
- if (va.va_name)
- kfree(va.va_name, MAXPATHLEN);
- if (fullpathptr)
- kfree(fullpathptr, MAXPATHLEN);
- if (vname)
- vnode_putname(vname);
- if (ab.base != NULL)
- FREE(ab.base, M_TEMP);
- if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL))
- kauth_acl_free(va.va_acl);
+ /*
+ * Always set the offset to the last succesful offset
+ * returned by VNOP_READDIR.
+ */
+ uio_setoffset(auio, fvd->fv_eoff);
- VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
- return(error);
+ return error;
}
+/* common attributes that only require KAUTH_VNODE_LIST_DIRECTORY */
+#define LIST_DIR_ATTRS (ATTR_CMN_NAME | ATTR_CMN_OBJTYPE | \
+ ATTR_CMN_FILEID | ATTR_CMN_RETURNED_ATTRS | \
+ ATTR_CMN_ERROR)
+
+/*
+ * int getattrlistbulk(int dirfd, struct attrlist *alist, void *attributeBuffer,
+ * size_t bufferSize, uint64_t options)
+ *
+ * Gets directory entries alongwith their attributes in the same way
+ * getattrlist does for a single file system object.
+ *
+ * On non error returns, retval will hold the count of entries returned.
+ */
int
-fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval)
+getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval)
{
- struct vfs_context *ctx;
- vnode_t vp = NULL;
- int error;
- struct getattrlist_args ap;
-
+ struct attrlist al;
+ vnode_t dvp = NULLVP;
+ struct fileproc *fp;
+ struct fd_vn_data *fvdata;
+ vfs_context_t ctx;
+ uthread_t ut;
+ enum uio_seg segflg;
+ int count;
+ uio_t auio = NULL;
+ char uio_buf[UIO_SIZEOF(1)];
+ kauth_action_t action;
+ int eofflag;
+ uint64_t options;
+ int error;
+
+ *retval = 0;
+
+ error = fp_getfvp(p, uap->dirfd, &fp, &dvp);
+ if (error) {
+ return error;
+ }
+
+ count = 0;
+ fvdata = NULL;
+ eofflag = 0;
ctx = vfs_context_current();
- error = 0;
+ ut = get_bsdthread_info(current_thread());
+ segflg = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32;
- if ((error = file_vnode(uap->fd, &vp)) != 0)
- return (error);
+ if ((fp->fp_glob->fg_flag & FREAD) == 0) {
+ /*
+ * AUDIT_ARG(vnpath_withref, dvp, ARG_VNODE1);
+ */
+ error = EBADF;
+ dvp = NULLVP;
+ goto out;
+ }
- if ((error = vnode_getwithref(vp)) != 0) {
- file_drop(uap->fd);
- return(error);
+ if ((error = vnode_getwithref(dvp))) {
+ dvp = NULLVP;
+ goto out;
}
- ap.path = 0;
- ap.alist = uap->alist;
- ap.attributeBuffer = uap->attributeBuffer;
- ap.bufferSize = uap->bufferSize;
- ap.options = uap->options;
+ if (uap->options & FSOPT_LIST_SNAPSHOT) {
+ vnode_t snapdvp;
- error = getattrlist_internal(vp, &ap, p, ctx);
+ if (!vnode_isvroot(dvp)) {
+ error = EINVAL;
+ goto out;
+ }
- file_drop(uap->fd);
- if (vp)
- vnode_put(vp);
+ /* switch directory to snapshot directory */
+ error = vnode_get_snapdir(dvp, &snapdvp, ctx);
+ if (error) {
+ goto out;
+ }
+ vnode_put(dvp);
+ dvp = snapdvp;
+ }
- return error;
-}
+ if (dvp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto out;
+ }
-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;
+#if CONFIG_MACF
+ error = mac_file_check_change_offset(vfs_context_ucred(ctx),
+ fp->fp_glob);
+ if (error) {
+ goto out;
+ }
+#endif
+ /*
+ * XXX : Audit Support
+ * AUDIT_ARG(vnpath, dvp, ARG_VNODE1);
+ */
- ctx = vfs_context_current();
- error = 0;
+ options = uap->options | FSOPT_ATTR_CMN_EXTENDED;
+
+ if ((error = copyin(CAST_USER_ADDR_T(uap->alist), &al,
+ sizeof(struct attrlist)))) {
+ goto out;
+ }
+
+ if (al.volattr ||
+ ((al.commonattr & ATTR_BULK_REQUIRED) != ATTR_BULK_REQUIRED)) {
+ error = EINVAL;
+ goto out;
+ }
+
+#if CONFIG_MACF
+ error = mac_vnode_check_readdir(ctx, dvp);
+ if (error != 0) {
+ goto out;
+ }
+#endif /* MAC */
/*
- * Look up the file.
+ * Requested attributes that are available in the direntry struct, with the addition
+ * of ATTR_CMN_RETURNED_ATTRS and ATTR_CMN_ERROR, can be let past with just LIST_DIRECTORY.
+ * Any other requested attributes require SEARCH as well.
*/
- nameiflags = NOTRIGGER | AUDITVNPATH1;
- if (!(uap->options & FSOPT_NOFOLLOW))
- nameiflags |= FOLLOW;
- NDINIT(&nd, LOOKUP, OP_GETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
+ action = KAUTH_VNODE_LIST_DIRECTORY;
+ if ((al.commonattr & ~LIST_DIR_ATTRS) || al.fileattr || al.dirattr) {
+ action |= KAUTH_VNODE_SEARCH;
+ }
- if ((error = namei(&nd)) != 0)
+ error = vnode_authorize(dvp, NULL, action, ctx);
+ if (error) {
goto out;
- vp = nd.ni_vp;
- nameidone(&nd);
+ }
+
+ fvdata = (struct fd_vn_data *)fp->fp_glob->fg_vn_data;
+ if (!fvdata) {
+ panic("Directory expected to have fg_vn_data");
+ }
- error = getattrlist_internal(vp, uap, p, ctx);
+ FV_LOCK(fvdata);
+
+ /*
+ * getattrlistbulk(2) maintains its offset in fv_offset. However
+ * if the offset in the file glob is set (or reset) to 0, the directory
+ * traversal needs to be restarted (Any existing state in the
+ * directory buffer is removed as well).
+ */
+ if (!fp->fp_glob->fg_offset) {
+ fvdata->fv_offset = 0;
+ kheap_free(KHEAP_DATA_BUFFERS, fvdata->fv_buf,
+ fvdata->fv_bufallocsiz);
+ fvdata->fv_bufsiz = 0;
+ fvdata->fv_bufdone = 0;
+ fvdata->fv_soff = 0;
+ fvdata->fv_eoff = 0;
+ fvdata->fv_eofflag = 0;
+ }
+
+ auio = uio_createwithbuffer(1, fvdata->fv_offset, segflg, UIO_READ,
+ &uio_buf[0], sizeof(uio_buf));
+ uio_addiov(auio, uap->attributeBuffer, (user_size_t)uap->bufferSize);
+
+ /*
+ * For "expensive" operations in which the native VNOP implementations
+ * end up having to do just as much (if not more) work than the default
+ * implementation, fall back to the default implementation.
+ * The VNOP helper functions depend on the filesystem providing the
+ * object type, if the caller has not requested ATTR_CMN_OBJTYPE, fall
+ * back to the default implementation.
+ */
+ if ((al.commonattr &
+ (ATTR_CMN_UUID | ATTR_CMN_GRPUUID | ATTR_CMN_EXTENDED_SECURITY)) ||
+ !(al.commonattr & ATTR_CMN_OBJTYPE)) {
+ error = ENOTSUP;
+ } else {
+ struct vnode_attr *va;
+ char *va_name;
+
+ if (fvdata->fv_eofflag && !fvdata->fv_buf) {
+ /*
+ * If the last successful VNOP_GETATTRLISTBULK or
+ * VNOP_READDIR returned EOF, don't try again.
+ */
+ eofflag = 1;
+ count = 0;
+ error = 0;
+ } else {
+ eofflag = 0;
+ count = 0;
+
+ va = kheap_alloc(KHEAP_TEMP, sizeof(struct vnode_attr), Z_WAITOK);
+
+ VATTR_INIT(va);
+ va_name = zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO);
+ va->va_name = va_name;
+
+ (void)getattrlist_setupvattr_all(&al, va, VNON, NULL,
+ IS_64BIT_PROCESS(p), (uap->options & FSOPT_ATTR_CMN_EXTENDED));
+
+ /*
+ * Set UT_KERN_RAGE_VNODES to cause all vnodes created by the
+ * filesystem to be rapidly aged.
+ */
+ ut->uu_flag |= UT_KERN_RAGE_VNODES;
+ error = VNOP_GETATTRLISTBULK(dvp, &al, va, auio, NULL,
+ options, &eofflag, &count, ctx);
+ ut->uu_flag &= ~UT_KERN_RAGE_VNODES;
+
+ zfree(ZV_NAMEI, va_name);
+ kheap_free(KHEAP_TEMP, va, sizeof(struct vnode_attr));
+
+ /*
+ * cache state of eofflag.
+ */
+ if (!error) {
+ fvdata->fv_eofflag = eofflag;
+ }
+ }
+ }
+
+ /*
+ * If the Filessytem does not natively support getattrlistbulk,
+ * do the default implementation.
+ */
+ if (error == ENOTSUP) {
+ eofflag = 0;
+ count = 0;
+
+ ut->uu_flag |= UT_KERN_RAGE_VNODES;
+ error = readdirattr(dvp, fvdata, auio, &al, options,
+ &count, &eofflag, ctx);
+ ut->uu_flag &= ~UT_KERN_RAGE_VNODES;
+ }
+
+ if (count) {
+ fvdata->fv_offset = uio_offset(auio);
+ fp->fp_glob->fg_offset = fvdata->fv_offset;
+ *retval = count;
+ error = 0;
+ } else if (!error && !eofflag) {
+ /*
+ * This just means the buffer was too small to fit even a
+ * single entry.
+ */
+ error = ERANGE;
+ }
+
+ FV_UNLOCK(fvdata);
out:
- if (vp)
- vnode_put(vp);
+ if (dvp) {
+ vnode_put(dvp);
+ }
+
+ file_drop(uap->dirfd);
+
return error;
}
attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
{
/* make sure we have enough source data */
- if ((*cursor) + size > end)
- return(EINVAL);
+ if ((*cursor) + size > end) {
+ return EINVAL;
+ }
bcopy(*cursor, buf, size);
*cursor += size;
- return(0);
+ return 0;
}
-#define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
-#define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = _f;} while(0)
-#define ATTR_UNPACK_TIME(v, is64) \
- do { \
- if (is64) { \
- struct user64_timespec us; \
- ATTR_UNPACK(us); \
- v.tv_sec = us.tv_sec; \
- v.tv_nsec = us.tv_nsec; \
- } else { \
- struct user32_timespec us; \
- ATTR_UNPACK(us); \
- v.tv_sec = us.tv_sec; \
- v.tv_nsec = us.tv_nsec; \
- } \
+#define ATTR_UNPACK(v) do {if ((error = attrlist_unpack_fixed(&cursor, bufend, &v, sizeof(v))) != 0) goto out;} while(0);
+#define ATTR_UNPACK_CAST(t, v) do { t _f; ATTR_UNPACK(_f); v = (typeof(v))_f;} while(0)
+#define ATTR_UNPACK_TIME(v, is64) \
+ do { \
+ if (is64) { \
+ struct user64_timespec us; \
+ ATTR_UNPACK(us); \
+ v.tv_sec = (unsigned long)us.tv_sec; \
+ v.tv_nsec = (long)us.tv_nsec; \
+ } else { \
+ struct user32_timespec us; \
+ ATTR_UNPACK(us); \
+ v.tv_sec = us.tv_sec; \
+ v.tv_nsec = us.tv_nsec; \
+ } \
} while(0)
struct attrlist al;
struct vnode_attr va;
struct attrreference ar;
- kauth_action_t action;
- char *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
- int proc_is64, error;
- uint32_t nace;
+ kauth_action_t action;
+ char *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
+ int proc_is64, error;
+ uint32_t nace;
kauth_filesec_t rfsec;
user_buf = NULL;
error = 0;
proc_is64 = proc_is64bit(p);
VATTR_INIT(&va);
-
+
/*
* Fetch the attribute set and validate.
*/
- if ((error = copyin(uap->alist, (caddr_t) &al, sizeof (al))))
+ if ((error = copyin(uap->alist, (caddr_t) &al, sizeof(al)))) {
goto out;
+ }
if (al.bitmapcount != ATTR_BIT_MAP_COUNT) {
error = EINVAL;
goto out;
}
+#if DEVELOPMENT || DEBUG
+ /*
+ * XXX VSWAP: Check for entitlements or special flag here
+ * so we can restrict access appropriately.
+ */
+#else /* DEVELOPMENT || DEBUG */
+
+ if (vnode_isswap(vp) && (ctx != vfs_context_kernel())) {
+ error = EPERM;
+ goto out;
+ }
+#endif /* DEVELOPMENT || DEBUG */
+
VFS_DEBUG(ctx, vp, "%p ATTRLIST - %s set common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
(uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
}
}
+ /*
+ * If the caller's bitmaps indicate that there are no attributes to set,
+ * then exit early.
+ */
+ 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
error = ENOMEM;
goto out;
}
- MALLOC(user_buf, char *, uap->bufferSize, M_TEMP, M_WAITOK);
+ user_buf = kheap_alloc(KHEAP_DATA_BUFFERS, uap->bufferSize, Z_WAITOK);
if (user_buf == NULL) {
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d bytes for buffer", uap->bufferSize);
error = ENOMEM;
#if CONFIG_MACF
error = mac_vnode_check_setattrlist(ctx, vp, &al);
- if (error)
+ if (error) {
goto out;
+ }
#endif /* MAC */
/*
}
if (al.commonattr & ATTR_CMN_CHGTIME) {
ATTR_UNPACK_TIME(va.va_change_time, proc_is64);
- VATTR_SET_ACTIVE(&va, va_change_time);
+ al.commonattr &= ~ATTR_CMN_CHGTIME;
+ /*quietly ignore change time; advisory in man page*/
}
if (al.commonattr & ATTR_CMN_ACCTIME) {
ATTR_UNPACK_TIME(va.va_access_time, proc_is64);
if (al.commonattr & ATTR_CMN_FLAGS) {
ATTR_UNPACK(va.va_flags);
VATTR_SET_ACTIVE(&va, va_flags);
+#if CONFIG_MACF
+ if ((error = mac_vnode_check_setflags(ctx, vp, va.va_flags)) != 0) {
+ goto out;
+ }
+#endif
}
if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) {
-
/*
* We are (for now) passed a kauth_filesec_t, but all we want from
* it is the ACL.
*/
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;
- if (((((char *)rfsec) + KAUTH_FILESEC_SIZE(0)) > bufend) || /* no space for acl */
+ if (((((char *)rfsec) + KAUTH_FILESEC_SIZE(0)) > bufend) || /* no space for acl */
(rfsec->fsec_magic != KAUTH_FILESEC_MAGIC) || /* bad magic */
(KAUTH_FILESEC_COPYSIZE(rfsec) != ar.attr_length) || /* size does not match */
- ((cp + KAUTH_FILESEC_COPYSIZE(rfsec)) > bufend)) { /* ACEs overrun buffer */
+ ((cp + KAUTH_FILESEC_COPYSIZE(rfsec)) > bufend)) { /* ACEs overrun buffer */
error = EINVAL;
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied", ar.attr_length);
goto out;
}
nace = rfsec->fsec_entrycount;
- if (nace == KAUTH_FILESEC_NOACL)
+ if (nace == KAUTH_FILESEC_NOACL) {
nace = 0;
- if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
+ }
+ if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
error = EINVAL;
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad ACL supplied");
goto out;
/* deleting ACL */
VATTR_SET(&va, va_acl, NULL);
} else {
-
- if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
+ if (nace > KAUTH_ACL_MAX_ENTRIES) { /* ACL size invalid */
error = EINVAL;
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: supplied ACL is too large");
goto out;
ATTR_UNPACK(va.va_guuid);
VATTR_SET_ACTIVE(&va, va_guuid);
}
+ if (al.commonattr & ATTR_CMN_ADDEDTIME) {
+ ATTR_UNPACK_TIME(va.va_addedtime, proc_is64);
+ VATTR_SET_ACTIVE(&va, va_addedtime);
+ }
+ /* Support setattrlist of data protection class */
+ if (al.commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) {
+ ATTR_UNPACK(va.va_dataprotect_class);
+ VATTR_SET_ACTIVE(&va, va_dataprotect_class);
+ }
/* volume */
if (al.volattr & ATTR_VOL_INFO) {
if (al.volattr & ATTR_VOL_NAME) {
volname = cursor;
ATTR_UNPACK(ar);
- volname += ar.attr_dataoffset;
- if ((volname + ar.attr_length) > bufend) {
+ /* attr_length cannot be 0! */
+ if ((ar.attr_dataoffset < 0) || (ar.attr_length == 0) ||
+ (ar.attr_length > uap->bufferSize) ||
+ (uap->bufferSize - ar.attr_length < (unsigned)ar.attr_dataoffset)) {
+ VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied (2) ", ar.attr_dataoffset);
+ error = EINVAL;
+ goto out;
+ }
+
+ if (volname >= bufend - ar.attr_dataoffset - ar.attr_length) {
error = EINVAL;
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume name too big for caller buffer");
goto out;
}
+ volname += ar.attr_dataoffset;
/* guarantee NUL termination */
volname[ar.attr_length - 1] = 0;
}
goto out;
}
+#if CONFIG_MACF
+ mac_vnode_notify_setattrlist(ctx, vp, &al);
+ if (VATTR_IS_ACTIVE(&va, va_flags)) {
+ mac_vnode_notify_setflags(ctx, vp, va.va_flags);
+ }
+#endif
+
/*
* Write the Finder Info if we have any.
*/
if (fndrinfo != NULL) {
if (al.volattr & ATTR_VOL_INFO) {
if (vp->v_tag == VT_HFS) {
+#define HFS_SET_BOOT_INFO (FCNTL_FS_SPECIFIC_BASE + 0x00005)
error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, ctx);
- if (error != 0)
+ if (error != 0) {
goto out;
+ }
} else {
/* XXX should never get here */
}
}
}
- /*
+ /*
* Set the volume name, if we have one
*/
- if (volname != NULL)
- {
+ if (volname != NULL) {
struct vfs_attr vs;
-
+
VFSATTR_INIT(&vs);
-
- vs.f_vol_name = volname; /* References the setattrlist buffer directly */
+
+ vs.f_vol_name = volname; /* References the setattrlist buffer directly */
VFSATTR_WANTED(&vs, f_vol_name);
-
+
#if CONFIG_MACF
error = mac_mount_check_setattr(ctx, vp->v_mount, &vs);
- if (error != 0)
+ if (error != 0) {
goto out;
+ }
#endif
if ((error = vfs_setattr(vp->v_mount, &vs, ctx)) != 0) {
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setting volume name failed");
goto out;
}
-
+
if (!VFSATTR_ALL_SUPPORTED(&vs)) {
error = EINVAL;
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not set volume name");
}
/* all done and successful */
-
+
out:
- if (user_buf != NULL)
- FREE(user_buf, M_TEMP);
+ kheap_free(KHEAP_DATA_BUFFERS, user_buf, uap->bufferSize);
VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d", error);
- return(error);
+ return error;
}
int
{
struct vfs_context *ctx;
struct nameidata nd;
- vnode_t vp = NULL;
- u_long nameiflags;
+ vnode_t vp = NULL;
+ uint32_t nameiflags;
int error = 0;
ctx = vfs_context_current();
* Look up the file.
*/
nameiflags = AUDITVNPATH1;
- if ((uap->options & FSOPT_NOFOLLOW) == 0)
+ if ((uap->options & FSOPT_NOFOLLOW) == 0) {
nameiflags |= FOLLOW;
+ }
NDINIT(&nd, LOOKUP, OP_SETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
- if ((error = namei(&nd)) != 0)
+ if ((error = namei(&nd)) != 0) {
goto out;
+ }
vp = nd.ni_vp;
nameidone(&nd);
error = setattrlist_internal(vp, uap, p, ctx);
out:
- if (vp != NULL)
+ if (vp != NULL) {
+ vnode_put(vp);
+ }
+ return error;
+}
+
+int
+setattrlistat(proc_t p, struct setattrlistat_args *uap, __unused int32_t *retval)
+{
+ struct setattrlist_args ap;
+ struct vfs_context *ctx;
+ struct nameidata nd;
+ vnode_t vp = NULLVP;
+ uint32_t nameiflags;
+ int error;
+
+ ctx = vfs_context_current();
+
+ AUDIT_ARG(fd, uap->fd);
+ /*
+ * Look up the file.
+ */
+ nameiflags = AUDITVNPATH1;
+ if (!(uap->options & FSOPT_NOFOLLOW)) {
+ nameiflags |= FOLLOW;
+ }
+ NDINIT(&nd, LOOKUP, OP_SETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
+ if ((error = nameiat(&nd, uap->fd)) != 0) {
+ goto out;
+ }
+ vp = nd.ni_vp;
+ nameidone(&nd);
+
+ ap.path = 0;
+ ap.alist = uap->alist;
+ ap.attributeBuffer = uap->attributeBuffer;
+ ap.bufferSize = uap->bufferSize;
+ ap.options = uap->options;
+
+ error = setattrlist_internal(vp, &ap, p, ctx);
+out:
+ if (vp) {
vnode_put(vp);
+ }
return error;
}
fsetattrlist(proc_t p, struct fsetattrlist_args *uap, __unused int32_t *retval)
{
struct vfs_context *ctx;
- vnode_t vp = NULL;
- int error;
+ vnode_t vp = NULL;
+ int error;
struct setattrlist_args ap;
ctx = vfs_context_current();
- if ((error = file_vnode(uap->fd, &vp)) != 0)
- return (error);
+ if ((error = file_vnode(uap->fd, &vp)) != 0) {
+ return error;
+ }
if ((error = vnode_getwithref(vp)) != 0) {
file_drop(uap->fd);
- return(error);
+ return error;
}
ap.path = 0;
error = setattrlist_internal(vp, &ap, p, ctx);
file_drop(uap->fd);
- if (vp != NULL)
+ if (vp != NULL) {
vnode_put(vp);
+ }
return error;
}
-