/*
- * Copyright (c) 1995-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 1995-2012 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
char *varcursor;
ssize_t allocated;
ssize_t needed;
+ attribute_set_t actual;
+ attribute_set_t valid;
};
/*
- * Pack (count) bytes from (source) into (buf).
+ * Attempt to pack a fixed width attribute of size (count) bytes from
+ * source to our attrlist buffer.
*/
static void
attrlist_pack_fixed(struct _attrlist_buf *ab, void *source, ssize_t count)
{
+ /*
+ * 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)
-{
- struct attrreference ar;
+attrlist_pack_variable2(struct _attrlist_buf *ab, const void *source, ssize_t count,
+ const void *ext, ssize_t extcount) {
+
+ /* Use ssize_t's for pointer math ease */
+ struct attrreference ar;
ssize_t fit;
- /* pack the reference to the variable object */
+ /*
+ * Pack the fixed-width component to the variable object.
+ * Note that we may be able to pack the fixed width attref, but not
+ * the variable (if there's no room).
+ */
ar.attr_dataoffset = ab->varcursor - ab->fixedcursor;
ar.attr_length = count + extcount;
attrlist_pack_fixed(ab, &ar, sizeof(ar));
- /* 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)
{
- 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;
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)
+ fit = lmin(count, space);
+ if (space > 0) {
+ /*
+ * If there is space remaining, copy data in, and
+ * accommodate the trailing NUL terminator.
+ *
+ * NOTE: if "space" is too small to hold the string and its NUL
+ * terminator (space < fit + 1), then the string value in the attribute
+ * buffer will NOT be NUL terminated!
+ *
+ * NOTE 2: bcopy() will do nothing if the length ("fit") is zero.
+ * Therefore, we don't bother checking for that here.
+ */
bcopy(source, ab->varcursor, fit);
- /* is there room for our trailing nul? */
- if (space > fit)
- ab->varcursor[fit] = '\0';
+ /* is there room for our trailing nul? */
+ if (space > fit) {
+ ab->varcursor[fit++] = '\0';
+ /* 'fit' now the number of bytes AFTER adding in the NUL */
+ }
+ }
+ /*
+ * always move in increments of 4 (including the trailing NUL)
+ */
+ ab->varcursor += roundup((count+1), 4);
- /* always move in increments of 4 */
- ab->varcursor += roundup(count + 1, 4);
}
#define ATTR_PACK4(AB, V) \
#define ATTR_PACK_TIME(b, v, is64) \
do { \
if (is64) { \
- struct user_timespec us = {v.tv_sec, v.tv_nsec}; \
+ struct user64_timespec us = {v.tv_sec, v.tv_nsec}; \
ATTR_PACK(&b, us); \
} else { \
- ATTR_PACK8(b, v); \
+ struct user32_timespec us = {v.tv_sec, v.tv_nsec}; \
+ ATTR_PACK(&b, us); \
} \
} while(0)
{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}
};
+#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_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}
vsp->f_active |= tab->bits;
if (tab->size == ATTR_TIME_SIZE) {
if (is_64bit) {
- *sizep += sizeof(struct user_timespec);
+ *sizep += sizeof(struct user64_timespec);
} else {
- *sizep += sizeof(struct timespec);
+ *sizep += sizeof(struct user32_timespec);
}
} else {
*sizep += tab->size;
* Parse the above tables.
*/
*sizep = sizeof(uint32_t); /* length count */
- if (alp->commonattr &&
- (error = getvolattrlist_parsetab(getvolattrlist_common_tab, alp->commonattr, vsp, sizep, is_64bit)) != 0)
- return(error);
+ if (alp->commonattr) {
+ if ((alp->commonattr & ATTR_CMN_VOL_INVALID) &&
+ (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) == 0) {
+ return (EINVAL);
+ }
+ if ((error = getvolattrlist_parsetab(getvolattrlist_common_tab,
+ alp->commonattr, vsp, sizep,
+ is_64bit)) != 0) {
+ return(error);
+ }
+ }
if (alp->volattr &&
(error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit)) != 0)
return(error);
return(0);
}
+/*
+ * Given the attributes listed in asp and those supported
+ * in the vsp, fixup the asp attributes to reflect any
+ * missing attributes from the file system
+ */
+static void
+getvolattrlist_fixupattrs(attribute_set_t *asp, struct vfs_attr *vsp)
+{
+ struct getvolattrlist_attrtab *tab;
+
+ if (asp->commonattr) {
+ tab = getvolattrlist_common_tab;
+ do {
+ if ((tab->attr & asp->commonattr) &&
+ (tab->bits != 0) &&
+ ((tab->bits & vsp->f_supported) == 0)) {
+ asp->commonattr &= ~tab->attr;
+ }
+ } while ((++tab)->attr != 0);
+ }
+ if (asp->volattr) {
+ tab = getvolattrlist_vol_tab;
+ do {
+ if ((tab->attr & asp->volattr) &&
+ (tab->bits != 0) &&
+ ((tab->bits & vsp->f_supported) == 0)) {
+ asp->volattr &= ~tab->attr;
+ }
+ } while ((++tab)->attr != 0);
+ }
+}
+
/*
* Table-driven setup for all valid common/dir/file/fork attributes against files.
*/
ssize_t size;
kauth_action_t action;
};
+
+/*
+ * A zero after the ATTR_ bit indicates that we don't expect the underlying FS to report back with this
+ * information, and we will synthesize it at the VFS level.
+ */
static struct getattrlist_attrtab getattrlist_common_tab[] = {
{ATTR_CMN_NAME, VATTR_BIT(va_name), sizeof(struct attrreference), KAUTH_VNODE_READ_ATTRIBUTES},
{ATTR_CMN_DEVID, 0, sizeof(dev_t), KAUTH_VNODE_READ_ATTRIBUTES},
{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}
};
+
static struct getattrlist_attrtab getattrlist_dir_tab[] = {
{ATTR_DIR_LINKCOUNT, VATTR_BIT(va_dirlinkcount), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
{ATTR_DIR_ENTRYCOUNT, VATTR_BIT(va_nchildren), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
- /* ATTR_DIR_ENTRYCOUNT falls back to va_nlink-2 if va_nchildren isn't supported, so request va_nlink just in case */
- {ATTR_DIR_ENTRYCOUNT, VATTR_BIT(va_nlink), 0, KAUTH_VNODE_READ_ATTRIBUTES},
{ATTR_DIR_MOUNTSTATUS, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
{0, 0, 0, 0}
};
ATTR_VOL_ALLOCATIONCLUMP | ATTR_VOL_IOBLOCKSIZE | \
ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS | \
ATTR_VOL_MOUNTEDDEVICE | ATTR_VOL_CAPABILITIES | \
- ATTR_VOL_ATTRIBUTES)
+ ATTR_VOL_ATTRIBUTES | ATTR_VOL_ENCODINGSUSED)
#define VFS_DFLT_ATTR_CMN (ATTR_CMN_NAME | ATTR_CMN_DEVID | \
ATTR_CMN_FSID | ATTR_CMN_OBJTYPE | \
ATTR_CMN_OBJTAG | ATTR_CMN_OBJID | \
ATTR_CMN_PAROBJID | ATTR_CMN_SCRIPT | \
ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | \
- ATTR_CMN_ACCTIME | ATTR_CMN_FNDRINFO | \
+ ATTR_CMN_FNDRINFO | \
ATTR_CMN_OWNERID | ATTR_CMN_GRPID | \
ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS | \
ATTR_CMN_USERACCESS | ATTR_CMN_FILEID | \
- ATTR_CMN_PARENTID)
+ ATTR_CMN_PARENTID | ATTR_CMN_RETURNED_ATTRS)
#define VFS_DFLT_ATTR_DIR (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
vap->va_active |= tab->bits;
if (tab->size == ATTR_TIME_SIZE) {
if (is_64bit) {
- *sizep += sizeof(struct user_timespec);
+ *sizep += sizeof(struct user64_timespec);
} else {
- *sizep += sizeof(struct timespec);
+ *sizep += sizeof(struct user32_timespec);
}
} else {
*sizep += tab->size;
}
*actionp |= tab->action;
+ if (attrs == recognised)
+ break; /* all done, get out */
}
} while ((++tab)->attr != 0);
return(0);
}
+/*
+ * Given the attributes listed in asp and those supported
+ * in the vap, fixup the asp attributes to reflect any
+ * missing attributes from the file system
+ */
+static void
+getattrlist_fixupattrs(attribute_set_t *asp, struct vnode_attr *vap)
+{
+ struct getattrlist_attrtab *tab;
+
+ if (asp->commonattr) {
+ tab = getattrlist_common_tab;
+ do {
+ /*
+ * This if() statement is slightly confusing. We're trying to
+ * iterate through all of the bits listed in the array
+ * getattr_common_tab, and see if the filesystem was expected
+ * to support it, and whether or not we need to do anything about this.
+ *
+ * This array is full of structs that have 4 fields (attr, bits, size, action).
+ * The first is used to store the ATTR_CMN_* bit that was being requested
+ * from userland. The second stores the VATTR_BIT corresponding to the field
+ * filled in vnode_attr struct. If it is 0, then we don't typically expect
+ * the filesystem to fill in this field. The third is the size of the field,
+ * and the fourth is the type of kauth actions needed.
+ *
+ * So, for all of the ATTR_CMN bits listed in this array, we iterate through
+ * them, and check to see if it was both passed down to the filesystem via the
+ * va_active bitfield, and whether or not we expect it to be emitted from
+ * the filesystem. If it wasn't supported, then we un-twiddle the bit and move
+ * on. This is done so that we can uncheck those bits and re-request
+ * a vnode_getattr from the filesystem again.
+ */
+ if ((tab->attr & asp->commonattr) &&
+ (tab->bits & vap->va_active) &&
+ (tab->bits & vap->va_supported) == 0) {
+ asp->commonattr &= ~tab->attr;
+ }
+ } while ((++tab)->attr != 0);
+ }
+ if (asp->dirattr) {
+ tab = getattrlist_dir_tab;
+ do {
+ if ((tab->attr & asp->dirattr) &&
+ (tab->bits & vap->va_active) &&
+ (vap->va_supported & tab->bits) == 0) {
+ asp->dirattr &= ~tab->attr;
+ }
+ } while ((++tab)->attr != 0);
+ }
+ if (asp->fileattr) {
+ tab = getattrlist_file_tab;
+ do {
+ if ((tab->attr & asp->fileattr) &&
+ (tab->bits & vap->va_active) &&
+ (vap->va_supported & tab->bits) == 0) {
+ asp->fileattr &= ~tab->attr;
+ }
+ } while ((++tab)->attr != 0);
+ }
+}
+
static int
setattrlist_setfinderinfo(vnode_t vp, char *fndrinfo, struct vfs_context *ctx)
{
static int
-getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp, vfs_context_t ctx, int is_64bit)
+getvolattrlist(vnode_t vp, struct getattrlist_args *uap, struct attrlist *alp,
+ vfs_context_t ctx, int is_64bit)
{
struct vfs_attr vs;
struct vnode_attr va;
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;
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);
+ if (pack_invalid) {
+ /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
+ if (!return_valid) {
+ error = EINVAL;
+ goto out;
+ }
+ /* Keep invalid attrs from being uninitialized */
+ bzero(&vs, sizeof (vs));
+ /* Generate a valid mask for post processing */
+ bcopy(&alp->commonattr, &ab.valid, sizeof (attribute_set_t));
+ }
+
/*
* For now, the vnode must be the root of its filesystem.
* To relax this, we need to be able to find the root vnode of a filesystem
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume attributes requested but not the root of a filesystem");
goto out;
}
-
+
/*
* Set up the vfs_attr structure and call the filesystem.
*/
/* check to see if our fixups were enough */
if (!VFSATTR_ALL_SUPPORTED(&vs)) {
- error = EINVAL;
- VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not get all requested volume attributes");
- VFS_DEBUG(ctx, vp, "ATTRLIST - wanted %016llx got %016llx missing %016llx",
- vs.f_active, vs.f_supported, vs.f_active & ~vs.f_supported);
- goto out;
+ if (return_valid) {
+ if (pack_invalid) {
+ /* Fix up valid mask for post processing */
+ getvolattrlist_fixupattrs(&ab.valid, &vs);
+
+ /* Force packing of everything asked for */
+ vs.f_supported = vs.f_active;
+ } else {
+ /* Adjust the requested attributes */
+ getvolattrlist_fixupattrs((attribute_set_t *)&alp->commonattr, &vs);
+ }
+ } else {
+ error = EINVAL;
+ goto out;
+ }
}
}
}
goto out;
}
- if (VATTR_IS_ACTIVE(&va, va_encoding) && !VATTR_IS_SUPPORTED(&va, va_encoding))
- VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
+ if (VATTR_IS_ACTIVE(&va, va_encoding) &&
+ !VATTR_IS_SUPPORTED(&va, va_encoding)) {
+ if (!return_valid || pack_invalid)
+ /* use kTextEncodingMacUnicode */
+ VATTR_RETURN(&va, va_encoding, 0x7e);
+ else
+ /* don't use a default */
+ alp->commonattr &= ~ATTR_CMN_SCRIPT;
+ }
}
/*
/* just use "/" as name */
cnp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
}
+ else {
+ release_str = 1;
+ }
cnl = strlen(cnp);
}
else {
* Note that since we won't ever copy out more than the caller requested,
* we never need to allocate more than they offer.
*/
- ab.allocated = imin(uap->bufferSize, fixedsize + varsize);
+ ab.allocated = ulmin(uap->bufferSize, fixedsize + varsize);
if (ab.allocated > ATTR_MAX_BUFFER) {
error = ENOMEM;
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
* Pack results into the destination buffer.
*/
ab.fixedcursor = ab.base + sizeof(uint32_t);
+ if (return_valid) {
+ ab.fixedcursor += sizeof (attribute_set_t);
+ bzero(&ab.actual, sizeof (ab.actual));
+ }
ab.varcursor = ab.base + fixedsize;
ab.needed = fixedsize + varsize;
/* common attributes **************************************************/
- if (alp->commonattr & ATTR_CMN_NAME)
+ if (alp->commonattr & ATTR_CMN_NAME) {
attrlist_pack_string(&ab, cnp, cnl);
- if (alp->commonattr & ATTR_CMN_DEVID)
+ ab.actual.commonattr |= ATTR_CMN_NAME;
+ }
+ if (alp->commonattr & ATTR_CMN_DEVID) {
ATTR_PACK4(ab, mnt->mnt_vfsstat.f_fsid.val[0]);
- if (alp->commonattr & ATTR_CMN_FSID)
+ ab.actual.commonattr |= ATTR_CMN_DEVID;
+ }
+ if (alp->commonattr & ATTR_CMN_FSID) {
ATTR_PACK8(ab, mnt->mnt_vfsstat.f_fsid);
- if (alp->commonattr & ATTR_CMN_OBJTYPE)
- ATTR_PACK4(ab, 0);
- if (alp->commonattr & ATTR_CMN_OBJTAG)
+ ab.actual.commonattr |= ATTR_CMN_FSID;
+ }
+ if (alp->commonattr & ATTR_CMN_OBJTYPE) {
+ if (!return_valid || pack_invalid)
+ ATTR_PACK4(ab, 0);
+ }
+ if (alp->commonattr & ATTR_CMN_OBJTAG) {
ATTR_PACK4(ab, vp->v_tag);
+ ab.actual.commonattr |= ATTR_CMN_OBJTAG;
+ }
if (alp->commonattr & ATTR_CMN_OBJID) {
- fsobj_id_t f = {0, 0};
- ATTR_PACK8(ab, f);
+ if (!return_valid || pack_invalid) {
+ fsobj_id_t f = {0, 0};
+ ATTR_PACK8(ab, f);
+ }
}
if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
- fsobj_id_t f = {0, 0};
- ATTR_PACK8(ab, f);
+ if (!return_valid || pack_invalid) {
+ fsobj_id_t f = {0, 0};
+ ATTR_PACK8(ab, f);
+ }
}
if (alp->commonattr & ATTR_CMN_PAROBJID) {
- fsobj_id_t f = {0, 0};
- ATTR_PACK8(ab, f);
+ if (!return_valid || pack_invalid) {
+ fsobj_id_t f = {0, 0};
+ ATTR_PACK8(ab, f);
+ }
}
/* note that this returns the encoding for the volume name, not the node name */
- if (alp->commonattr & ATTR_CMN_SCRIPT)
+ if (alp->commonattr & ATTR_CMN_SCRIPT) {
ATTR_PACK4(ab, va.va_encoding);
- if (alp->commonattr & ATTR_CMN_CRTIME)
+ ab.actual.commonattr |= ATTR_CMN_SCRIPT;
+ }
+ if (alp->commonattr & ATTR_CMN_CRTIME) {
ATTR_PACK_TIME(ab, vs.f_create_time, is_64bit);
- if (alp->commonattr & ATTR_CMN_MODTIME)
- ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
- if (alp->commonattr & ATTR_CMN_CHGTIME)
+ ab.actual.commonattr |= ATTR_CMN_CRTIME;
+ }
+ if (alp->commonattr & ATTR_CMN_MODTIME) {
ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
- if (alp->commonattr & ATTR_CMN_ACCTIME)
+ ab.actual.commonattr |= ATTR_CMN_MODTIME;
+ }
+ if (alp->commonattr & ATTR_CMN_CHGTIME) {
+ if (!return_valid || pack_invalid)
+ ATTR_PACK_TIME(ab, vs.f_modify_time, is_64bit);
+ }
+ if (alp->commonattr & ATTR_CMN_ACCTIME) {
ATTR_PACK_TIME(ab, vs.f_access_time, is_64bit);
- if (alp->commonattr & ATTR_CMN_BKUPTIME)
+ ab.actual.commonattr |= ATTR_CMN_ACCTIME;
+ }
+ if (alp->commonattr & ATTR_CMN_BKUPTIME) {
ATTR_PACK_TIME(ab, vs.f_backup_time, is_64bit);
+ ab.actual.commonattr |= ATTR_CMN_BKUPTIME;
+ }
if (alp->commonattr & ATTR_CMN_FNDRINFO) {
char f[32];
/*
* This attribute isn't really Finder Info, at least for HFS.
*/
if (vp->v_tag == VT_HFS) {
- if ((error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, (caddr_t)&f, 0, ctx)) != 0)
+ error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, (caddr_t)&f, 0, ctx);
+ if (error == 0) {
+ attrlist_pack_fixed(&ab, f, sizeof(f));
+ ab.actual.commonattr |= ATTR_CMN_FNDRINFO;
+ } else if (!return_valid) {
goto out;
- } else {
+ }
+ } else if (!return_valid || pack_invalid) {
/* XXX we could at least pass out the volume UUID here */
bzero(&f, sizeof(f));
+ attrlist_pack_fixed(&ab, f, sizeof(f));
}
- attrlist_pack_fixed(&ab, f, sizeof(f));
}
- if (alp->commonattr & ATTR_CMN_OWNERID)
+ if (alp->commonattr & ATTR_CMN_OWNERID) {
ATTR_PACK4(ab, va.va_uid);
- if (alp->commonattr & ATTR_CMN_GRPID)
+ ab.actual.commonattr |= ATTR_CMN_OWNERID;
+ }
+ if (alp->commonattr & ATTR_CMN_GRPID) {
ATTR_PACK4(ab, va.va_gid);
- if (alp->commonattr & ATTR_CMN_ACCESSMASK)
+ ab.actual.commonattr |= ATTR_CMN_GRPID;
+ }
+ if (alp->commonattr & ATTR_CMN_ACCESSMASK) {
ATTR_PACK_CAST(&ab, uint32_t, va.va_mode);
- if (alp->commonattr & ATTR_CMN_FLAGS)
+ ab.actual.commonattr |= ATTR_CMN_ACCESSMASK;
+ }
+ if (alp->commonattr & ATTR_CMN_FLAGS) {
ATTR_PACK4(ab, va.va_flags);
+ ab.actual.commonattr |= ATTR_CMN_FLAGS;
+ }
if (alp->commonattr & ATTR_CMN_USERACCESS) { /* XXX this is expensive and also duplicate work */
uint32_t perms = 0;
if (vnode_isdir(vp)) {
#endif /* MAC */
KAUTH_DEBUG("ATTRLIST - returning user access %x", perms);
ATTR_PACK4(ab, perms);
+ ab.actual.commonattr |= ATTR_CMN_USERACCESS;
+ }
+ /*
+ * The following common volume attributes are only
+ * packed when the pack_invalid mode is enabled.
+ */
+ if (pack_invalid) {
+ uint64_t fid = 0;
+
+ if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY)
+ attrlist_pack_variable(&ab, NULL, 0);
+ if (alp->commonattr & ATTR_CMN_UUID)
+ ATTR_PACK(&ab, kauth_null_guid);
+ if (alp->commonattr & ATTR_CMN_GRPUUID)
+ ATTR_PACK(&ab, kauth_null_guid);
+ if (alp->commonattr & ATTR_CMN_FILEID)
+ ATTR_PACK8(ab, fid);
+ if (alp->commonattr & ATTR_CMN_PARENTID)
+ ATTR_PACK8(ab, fid);
}
/* volume attributes **************************************************/
- if (alp->volattr & ATTR_VOL_FSTYPE)
+ if (alp->volattr & ATTR_VOL_FSTYPE) {
ATTR_PACK_CAST(&ab, uint32_t, vfs_typenum(mnt));
- if (alp->volattr & ATTR_VOL_SIGNATURE)
+ ab.actual.volattr |= ATTR_VOL_FSTYPE;
+ }
+ if (alp->volattr & ATTR_VOL_SIGNATURE) {
ATTR_PACK_CAST(&ab, uint32_t, vs.f_signature);
- if (alp->volattr & ATTR_VOL_SIZE)
+ ab.actual.volattr |= ATTR_VOL_SIGNATURE;
+ }
+ if (alp->volattr & ATTR_VOL_SIZE) {
ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_blocks);
- if (alp->volattr & ATTR_VOL_SPACEFREE)
+ ab.actual.volattr |= ATTR_VOL_SIZE;
+ }
+ if (alp->volattr & ATTR_VOL_SPACEFREE) {
ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bfree);
- if (alp->volattr & ATTR_VOL_SPACEAVAIL)
+ ab.actual.volattr |= ATTR_VOL_SPACEFREE;
+ }
+ if (alp->volattr & ATTR_VOL_SPACEAVAIL) {
ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_bavail);
- if (alp->volattr & ATTR_VOL_MINALLOCATION)
+ ab.actual.volattr |= ATTR_VOL_SPACEAVAIL;
+ }
+ if (alp->volattr & ATTR_VOL_MINALLOCATION) {
ATTR_PACK_CAST(&ab, off_t, vs.f_bsize);
- if (alp->volattr & ATTR_VOL_ALLOCATIONCLUMP)
+ ab.actual.volattr |= ATTR_VOL_MINALLOCATION;
+ }
+ if (alp->volattr & ATTR_VOL_ALLOCATIONCLUMP) {
ATTR_PACK_CAST(&ab, off_t, vs.f_bsize); /* not strictly true */
- if (alp->volattr & ATTR_VOL_IOBLOCKSIZE)
+ ab.actual.volattr |= ATTR_VOL_ALLOCATIONCLUMP;
+ }
+ if (alp->volattr & ATTR_VOL_IOBLOCKSIZE) {
ATTR_PACK_CAST(&ab, uint32_t, vs.f_iosize);
- if (alp->volattr & ATTR_VOL_OBJCOUNT)
+ ab.actual.volattr |= ATTR_VOL_IOBLOCKSIZE;
+ }
+ if (alp->volattr & ATTR_VOL_OBJCOUNT) {
ATTR_PACK_CAST(&ab, uint32_t, vs.f_objcount);
- if (alp->volattr & ATTR_VOL_FILECOUNT)
+ ab.actual.volattr |= ATTR_VOL_OBJCOUNT;
+ }
+ if (alp->volattr & ATTR_VOL_FILECOUNT) {
ATTR_PACK_CAST(&ab, uint32_t, vs.f_filecount);
- if (alp->volattr & ATTR_VOL_DIRCOUNT)
+ ab.actual.volattr |= ATTR_VOL_FILECOUNT;
+ }
+ if (alp->volattr & ATTR_VOL_DIRCOUNT) {
ATTR_PACK_CAST(&ab, uint32_t, vs.f_dircount);
- if (alp->volattr & ATTR_VOL_MAXOBJCOUNT)
+ ab.actual.volattr |= ATTR_VOL_DIRCOUNT;
+ }
+ if (alp->volattr & ATTR_VOL_MAXOBJCOUNT) {
ATTR_PACK_CAST(&ab, uint32_t, vs.f_maxobjcount);
- if (alp->volattr & ATTR_VOL_MOUNTPOINT)
+ ab.actual.volattr |= ATTR_VOL_MAXOBJCOUNT;
+ }
+ if (alp->volattr & ATTR_VOL_MOUNTPOINT) {
attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntonname, 0);
- if (alp->volattr & ATTR_VOL_NAME)
+ ab.actual.volattr |= ATTR_VOL_MOUNTPOINT;
+ }
+ if (alp->volattr & ATTR_VOL_NAME) {
attrlist_pack_string(&ab, vs.f_vol_name, 0);
- if (alp->volattr & ATTR_VOL_MOUNTFLAGS)
+ ab.actual.volattr |= ATTR_VOL_NAME;
+ }
+ if (alp->volattr & ATTR_VOL_MOUNTFLAGS) {
ATTR_PACK_CAST(&ab, uint32_t, mnt->mnt_flag);
- if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE)
+ ab.actual.volattr |= ATTR_VOL_MOUNTFLAGS;
+ }
+ if (alp->volattr & ATTR_VOL_MOUNTEDDEVICE) {
attrlist_pack_string(&ab, mnt->mnt_vfsstat.f_mntfromname, 0);
- if (alp->volattr & ATTR_VOL_ENCODINGSUSED)
- ATTR_PACK_CAST(&ab, uint64_t, ~0LL); /* return all encodings */
+ ab.actual.volattr |= ATTR_VOL_MOUNTEDDEVICE;
+ }
+ if (alp->volattr & ATTR_VOL_ENCODINGSUSED) {
+ if (!return_valid || pack_invalid)
+ ATTR_PACK_CAST(&ab, uint64_t, ~0LL); /* return all encodings */
+ }
if (alp->volattr & ATTR_VOL_CAPABILITIES) {
/* fix up volume capabilities */
if (vfs_extendedsecurity(mnt)) {
}
vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
ATTR_PACK(&ab, vs.f_capabilities);
+ ab.actual.volattr |= ATTR_VOL_CAPABILITIES;
+ }
+ if (alp->volattr & ATTR_VOL_UUID) {
+ ATTR_PACK(&ab, vs.f_uuid);
+ ab.actual.volattr |= ATTR_VOL_UUID;
}
if (alp->volattr & ATTR_VOL_ATTRIBUTES) {
/* fix up volume attribute information */
vs.f_attributes.nativeattr.commonattr &= ~(ATTR_CMN_EXTENDED_SECURITY | ATTR_CMN_UUID | ATTR_CMN_GRPUUID);
}
ATTR_PACK(&ab, vs.f_attributes);
+ ab.actual.volattr |= ATTR_VOL_ATTRIBUTES;
}
/* diagnostic */
- if ((ab.fixedcursor - ab.base) != fixedsize)
- panic("packed field size mismatch; allocated %ld but packed %d for common %08x vol %08x",
- fixedsize, ab.fixedcursor - ab.base, alp->commonattr, alp->volattr);
- if (ab.varcursor != (ab.base + ab.needed))
- panic("packed variable field size mismatch; used %d but expected %ld", ab.varcursor - ab.base, ab.needed);
+ if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize)
+ panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
+ fixedsize, (long) (ab.fixedcursor - ab.base), alp->commonattr, alp->volattr);
+ if (!return_valid && ab.varcursor != (ab.base + ab.needed))
+ panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab.varcursor - ab.base), ab.needed);
/*
* In the compatible case, we report the smaller of the required and returned sizes.
*/
*(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
+ /* Return attribute set output if requested. */
+ if (return_valid) {
+ ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS;
+ if (pack_invalid) {
+ /* Only report the attributes that are valid */
+ ab.actual.commonattr &= ab.valid.commonattr;
+ ab.actual.volattr &= ab.valid.volattr;
+ }
+ bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual));
+ }
error = copyout(ab.base, uap->attributeBuffer, ab.allocated);
out:
if (vs.f_vol_name != NULL)
kfree(vs.f_vol_name, MAXPATHLEN);
+ if (release_str) {
+ vnode_putname(cnp);
+ }
if (ab.base != NULL)
FREE(ab.base, M_TEMP);
VFS_DEBUG(ctx, vp, "ATTRLIST - returning %d", error);
/*
* Obtain attribute information about a filesystem object.
*/
-int
-getattrlist(proc_t p, struct getattrlist_args *uap, __unused register_t *retval)
+
+static int
+getattrlist_internal(vnode_t vp, struct getattrlist_args *uap,
+ __unused struct componentname *getattr_name, proc_t p, vfs_context_t ctx)
{
struct attrlist al;
struct vnode_attr va;
- struct vfs_context *ctx;
- struct nameidata nd;
struct _attrlist_buf ab;
- vnode_t vp;
- u_long nameiflags;
kauth_action_t action;
ssize_t fixedsize, varsize;
const char *cnp;
const char *vname = NULL;
+ 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;
- ctx = vfs_context_current();
- vp = NULL;
- error = 0;
proc_is64 = proc_is64bit(p);
VATTR_INIT(&va);
va.va_name = NULL;
ab.base = NULL;
cnp = "unknown";
cnl = 0;
-
- /*
- * Look up the file.
- */
- nameiflags = NOTRIGGER | AUDITVNPATH1;
- if (!(uap->options & FSOPT_NOFOLLOW))
- nameiflags |= FOLLOW;
- NDINIT(&nd, LOOKUP, nameiflags, UIO_USERSPACE, uap->path, ctx);
-
- if ((error = namei(&nd)) != 0)
- goto out;
- vp = nd.ni_vp;
- nameidone(&nd);
+ fullpathptr = NULL;
+ fullpathlen = 0;
/*
* Fetch the attribute request.
VFS_DEBUG(ctx, vp, "%p ATTRLIST - %s request common %08x vol %08x file %08x dir %08x fork %08x %sfollow on '%s'",
vp, p->p_comm, al.commonattr, al.volattr, al.fileattr, al.dirattr, al.forkattr,
(uap->options & FSOPT_NOFOLLOW) ? "no":"", vp->v_name);
-
+
#if CONFIG_MACF
error = mac_vnode_check_getattrlist(ctx, vp, &al);
if (error)
goto out;
}
+ /* Check for special packing semantics */
+ return_valid = (al.commonattr & ATTR_CMN_RETURNED_ATTRS);
+ pack_invalid = (uap->options & FSOPT_PACK_INVAL_ATTRS);
+ if (pack_invalid) {
+ /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
+ if (!return_valid || al.forkattr) {
+ error = EINVAL;
+ goto out;
+ }
+ /* Keep invalid attrs from being uninitialized */
+ bzero(&va, sizeof (va));
+ /* Generate a valid mask for post processing */
+ bcopy(&al.commonattr, &ab.valid, sizeof (attribute_set_t));
+ }
+
+ /* Pick up the vnode type. If the FS is bad and changes vnode types on us, we
+ * will have a valid snapshot that we can work from here.
+ */
+ vtype = vp->v_type;
+
+
/*
* Set up the vnode_attr structure and authorise.
*/
- if ((error = getattrlist_setupvattr(&al, &va, &fixedsize, &action, proc_is64, vnode_isdir(vp))) != 0) {
+ if ((error = getattrlist_setupvattr(&al, &va, &fixedsize, &action, proc_is64, (vtype == VDIR))) != 0) {
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
goto out;
}
goto out;
}
+ /*
+ * If we're asking for the full path, allocate a buffer for that.
+ */
+ if (al.commonattr & (ATTR_CMN_FULLPATH)) {
+ fullpathptr = (char*) kalloc(MAXPATHLEN);
+ if (fullpathptr == NULL) {
+ error = ENOMEM;
+ VFS_DEBUG(ctx,vp, "ATTRLIST - ERROR: cannot allocate fullpath buffer");
+ goto out;
+ }
+ }
+
+
if (va.va_active != 0) {
/*
* If we're going to ask for va_name, allocate a buffer to point it at
*/
if ((al.commonattr & (ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID | ATTR_CMN_FILEID)) && !VATTR_IS_SUPPORTED(&va, va_linkid))
VATTR_CLEAR_ACTIVE(&va, va_linkid); /* forget we wanted this */
+
/*
- * Many (most?) filesystems don't know their parent object id. We can get it the
- * hard way.
+ * Many filesystems don't know their parent object id.
+ * If necessary, attempt to derive it from the vnode.
*/
- if ((al.commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)) && !VATTR_IS_SUPPORTED(&va, va_parentid))
- VATTR_CLEAR_ACTIVE(&va, va_parentid);
+ if ((al.commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)) &&
+ !VATTR_IS_SUPPORTED(&va, va_parentid)) {
+ vnode_t dvp;
+
+ if ((dvp = vnode_getparent(vp)) != NULLVP) {
+ struct vnode_attr lva;
+
+ VATTR_INIT(&lva);
+ VATTR_WANTED(&lva, va_fileid);
+ if (vnode_getattr(dvp, &lva, ctx) == 0 &&
+ VATTR_IS_SUPPORTED(&va, va_fileid)) {
+ va.va_parentid = lva.va_fileid;
+ VATTR_SET_SUPPORTED(&va, va_parentid);
+ }
+ vnode_put(dvp);
+ }
+ }
/*
* And we can report datasize/alloc from total.
*/
/*
* If we don't have an encoding, go with UTF-8
*/
- if ((al.commonattr & ATTR_CMN_SCRIPT) && !VATTR_IS_SUPPORTED(&va, va_encoding))
+ if ((al.commonattr & ATTR_CMN_SCRIPT) &&
+ !VATTR_IS_SUPPORTED(&va, va_encoding) && !return_valid)
VATTR_RETURN(&va, va_encoding, 0x7e /* kTextEncodingMacUnicode */);
/*
/* check again */
if (!VATTR_ALL_SUPPORTED(&va)) {
- error = EINVAL;
- VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not get all requested file attributes");
- VFS_DEBUG(ctx, vp, "ATTRLIST - have %016llx wanted %016llx missing %016llx",
- va.va_supported, va.va_active, va.va_active & ~va.va_supported);
- goto out;
+ if (return_valid) {
+ if (pack_invalid) {
+ /* Fix up valid mask for post processing */
+ getattrlist_fixupattrs(&ab.valid, &va);
+
+ /* Force packing of everything asked for */
+ va.va_supported = va.va_active;
+ } else {
+ /* Adjust the requested attributes */
+ getattrlist_fixupattrs((attribute_set_t *)&al.commonattr, &va);
+ }
+ } else {
+ error = EINVAL;
+ goto out;
+ }
}
}
}
/*
* Compute variable-space requirements.
*/
- varsize = 0; /* length count */
+ varsize = 0; /* length count */
+
+ /* We may need to fix up the name attribute if requested */
if (al.commonattr & ATTR_CMN_NAME) {
if (VATTR_IS_SUPPORTED(&va, va_name)) {
va.va_name[MAXPATHLEN-1] = '\0'; /* Ensure nul-termination */
cnp = va.va_name;
cnl = strlen(cnp);
- } else {
+ }
+ else {
+ /* Filesystem did not support getting the name */
if (vnode_isvroot(vp)) {
if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
- vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
+ vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
/* special case for boot volume. Use root name when it's
* available (which is the volume name) or just the mount on
* name of "/". we must do this for binary compatibility with
else {
getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, &cnp, &cnl);
}
- } else {
+ }
+ else {
cnp = vname = vnode_getname(vp);
cnl = 0;
if (cnp != NULL) {
varsize += roundup(cnl + 1, 4);
}
+ /*
+ * Compute the full path to this vnode, if necessary. This attribute is almost certainly
+ * not supported by any filesystem, so build the path to this vnode at this time.
+ */
+ if (al.commonattr & ATTR_CMN_FULLPATH) {
+ int len = MAXPATHLEN;
+ int err;
+ /* call build_path making sure NOT to use the cache-only behavior */
+ err = build_path(vp, fullpathptr, len, &len, 0, vfs_context_current());
+ if (err) {
+ error = err;
+ goto out;
+ }
+ fullpathlen = 0;
+ if (fullpathptr){
+ fullpathlen = strlen(fullpathptr);
+ }
+ varsize += roundup(fullpathlen+1, 4);
+ }
+
/*
* We have a kauth_acl_t but we will be returning a kauth_filesec_t.
*
* user-space this is OK.
*/
if ((al.commonattr & ATTR_CMN_EXTENDED_SECURITY) &&
- VATTR_IS_SUPPORTED(&va, va_acl) &&
- (va.va_acl != NULL))
- varsize += roundup(KAUTH_FILESEC_SIZE(va.va_acl->acl_entrycount), 4);
-
+ VATTR_IS_SUPPORTED(&va, va_acl) &&
+ (va.va_acl != NULL)) {
+
+ /*
+ * Since we have a kauth_acl_t (not a kauth_filesec_t), we have to check against
+ * KAUTH_FILESEC_NOACL ourselves
+ */
+ if (va.va_acl->acl_entrycount == KAUTH_FILESEC_NOACL) {
+ varsize += roundup((KAUTH_FILESEC_SIZE(0)), 4);
+ }
+ else {
+ varsize += roundup ((KAUTH_FILESEC_SIZE(va.va_acl->acl_entrycount)), 4);
+ }
+ }
+
/*
* Allocate a target buffer for attribute results.
*
* don't result in a panic if the caller's buffer is too small..
*/
ab.allocated = fixedsize + varsize;
- if (ab.allocated > ATTR_MAX_BUFFER) {
+ /* Cast 'allocated' to an unsigned to verify allocation size */
+ if ( ((size_t)ab.allocated) > ATTR_MAX_BUFFER) {
error = ENOMEM;
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
goto out;
* 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 **************************************************/
- if (al.commonattr & ATTR_CMN_NAME)
+ if (al.commonattr & ATTR_CMN_NAME) {
attrlist_pack_string(&ab, cnp, cnl);
- if (al.commonattr & ATTR_CMN_DEVID)
+ ab.actual.commonattr |= ATTR_CMN_NAME;
+ }
+ if (al.commonattr & ATTR_CMN_DEVID) {
ATTR_PACK4(ab, vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
- if (al.commonattr & ATTR_CMN_FSID)
+ ab.actual.commonattr |= ATTR_CMN_DEVID;
+ }
+ if (al.commonattr & ATTR_CMN_FSID) {
ATTR_PACK8(ab, vp->v_mount->mnt_vfsstat.f_fsid);
- if (al.commonattr & ATTR_CMN_OBJTYPE)
- ATTR_PACK4(ab, vp->v_type);
- if (al.commonattr & ATTR_CMN_OBJTAG)
+ ab.actual.commonattr |= ATTR_CMN_FSID;
+ }
+ if (al.commonattr & ATTR_CMN_OBJTYPE) {
+ ATTR_PACK4(ab, vtype);
+ ab.actual.commonattr |= ATTR_CMN_OBJTYPE;
+ }
+ if (al.commonattr & ATTR_CMN_OBJTAG) {
ATTR_PACK4(ab, vp->v_tag);
+ ab.actual.commonattr |= ATTR_CMN_OBJTAG;
+ }
if (al.commonattr & ATTR_CMN_OBJID) {
fsobj_id_t f;
/*
}
f.fid_generation = 0;
ATTR_PACK8(ab, f);
+ ab.actual.commonattr |= ATTR_CMN_OBJID;
}
if (al.commonattr & ATTR_CMN_OBJPERMANENTID) {
fsobj_id_t f;
}
f.fid_generation = 0;
ATTR_PACK8(ab, f);
+ ab.actual.commonattr |= ATTR_CMN_OBJPERMANENTID;
}
if (al.commonattr & ATTR_CMN_PAROBJID) {
fsobj_id_t f;
- /*
- * If the filesystem doesn't know the parent ID, we can
- * try to get it via v->v_parent. Don't need to worry
- * about links here, as we dont allow hardlinks to
- * directories.
- */
- if (VATTR_IS_SUPPORTED(&va, va_parentid)) {
- f.fid_objno = va.va_parentid;
- } else {
- struct vnode_attr lva;
- vnode_t pvp;
-
- pvp = vnode_getparent(vp);
-
- if (pvp == NULLVP) {
- error = EINVAL;
- goto out;
- }
- VATTR_INIT(&lva);
- VATTR_WANTED(&lva, va_fileid);
- error = vnode_getattr(pvp, &lva, ctx);
- vnode_put(pvp);
- if (error != 0)
- goto out;
- f.fid_objno = lva.va_fileid;
- }
+ f.fid_objno = va.va_parentid; /* could be lossy here! */
f.fid_generation = 0;
ATTR_PACK8(ab, f);
+ ab.actual.commonattr |= ATTR_CMN_PAROBJID;
}
- if (al.commonattr & ATTR_CMN_SCRIPT)
- ATTR_PACK4(ab, va.va_encoding);
- if (al.commonattr & ATTR_CMN_CRTIME)
+ if (al.commonattr & ATTR_CMN_SCRIPT) {
+ if (VATTR_IS_SUPPORTED(&va, va_encoding)) {
+ ATTR_PACK4(ab, va.va_encoding);
+ ab.actual.commonattr |= ATTR_CMN_SCRIPT;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4(ab, 0x7e);
+ }
+ }
+ if (al.commonattr & ATTR_CMN_CRTIME) {
ATTR_PACK_TIME(ab, va.va_create_time, proc_is64);
- if (al.commonattr & ATTR_CMN_MODTIME)
+ ab.actual.commonattr |= ATTR_CMN_CRTIME;
+ }
+ if (al.commonattr & ATTR_CMN_MODTIME) {
ATTR_PACK_TIME(ab, va.va_modify_time, proc_is64);
- if (al.commonattr & ATTR_CMN_CHGTIME)
+ ab.actual.commonattr |= ATTR_CMN_MODTIME;
+ }
+ if (al.commonattr & ATTR_CMN_CHGTIME) {
ATTR_PACK_TIME(ab, va.va_change_time, proc_is64);
- if (al.commonattr & ATTR_CMN_ACCTIME)
+ ab.actual.commonattr |= ATTR_CMN_CHGTIME;
+ }
+ if (al.commonattr & ATTR_CMN_ACCTIME) {
ATTR_PACK_TIME(ab, va.va_access_time, proc_is64);
- if (al.commonattr & ATTR_CMN_BKUPTIME)
+ ab.actual.commonattr |= ATTR_CMN_ACCTIME;
+ }
+ if (al.commonattr & ATTR_CMN_BKUPTIME) {
ATTR_PACK_TIME(ab, va.va_backup_time, proc_is64);
- if (al.commonattr & ATTR_CMN_FNDRINFO) {
- uio_t auio;
- size_t fisize;
- char uio_buf[UIO_SIZEOF(1)];
-
- fisize = imin(32, ab.allocated - (ab.fixedcursor - ab.base));
- if (fisize > 0) {
- if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, uio_buf, sizeof(uio_buf))) == NULL) {
- error = ENOMEM;
- goto out;
- } else {
- uio_addiov(auio, CAST_USER_ADDR_T(ab.fixedcursor), fisize);
- error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio, &fisize, XATTR_NOSECURITY, ctx);
- uio_free(auio);
- }
- if (error != 0) {
- if ((error == ENOATTR) || (error == ENOENT) || (error == ENOTSUP) || (error == EPERM)) {
- VFS_DEBUG(ctx, vp, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
- bzero(ab.fixedcursor, 32);
- error = 0;
- } else {
- VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: reading system.finderinfo attribute");
- goto out;
- }
- }
- } else {
- VFS_DEBUG(ctx, vp, "ATTRLIST - no room in caller buffer for FINDERINFO");
- }
- ab.fixedcursor += 32;
+ ab.actual.commonattr |= ATTR_CMN_BKUPTIME;
}
- if (al.commonattr & ATTR_CMN_OWNERID)
- ATTR_PACK4(ab, va.va_uid);
- if (al.commonattr & ATTR_CMN_GRPID)
- ATTR_PACK4(ab, va.va_gid);
- if (al.commonattr & ATTR_CMN_ACCESSMASK)
- ATTR_PACK4(ab, va.va_mode);
- if (al.commonattr & ATTR_CMN_FLAGS)
- ATTR_PACK4(ab, va.va_flags);
+ /*
+ * They are requesting user access, we should obtain this before getting
+ * the finder info. For some network file systems this is a performance
+ * improvement.
+ */
if (al.commonattr & ATTR_CMN_USERACCESS) { /* this is expensive */
- uint32_t perms = 0;
- if (vnode_isdir(vp)) {
+ if (vtype == VDIR) {
if (vnode_authorize(vp, NULL,
- KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE | KAUTH_VNODE_ADD_SUBDIRECTORY | KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
+ KAUTH_VNODE_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_EXECUTE, ctx) == 0)
perms |= X_OK;
}
+ }
+
+ if (al.commonattr & ATTR_CMN_FNDRINFO) {
+ uio_t auio;
+ size_t fisize = 32;
+ char uio_buf[UIO_SIZEOF(1)];
+ if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
+ uio_buf, sizeof(uio_buf))) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ uio_addiov(auio, CAST_USER_ADDR_T(ab.fixedcursor), fisize);
+ error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio,
+ &fisize, XATTR_NOSECURITY, ctx);
+ uio_free(auio);
+ /*
+ * Default to zeros if its not available,
+ * unless ATTR_CMN_RETURNED_ATTRS was requested.
+ */
+ if (error &&
+ (!return_valid || pack_invalid) &&
+ ((error == ENOATTR) || (error == ENOENT) ||
+ (error == ENOTSUP) || (error == EPERM))) {
+ VFS_DEBUG(ctx, vp, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
+ bzero(ab.fixedcursor, 32);
+ error = 0;
+ }
+ if (error == 0) {
+ ab.fixedcursor += 32;
+ ab.actual.commonattr |= ATTR_CMN_FNDRINFO;
+ } else if (!return_valid) {
+ VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: reading system.finderinfo attribute");
+ goto out;
+ }
+ }
+ if (al.commonattr & ATTR_CMN_OWNERID) {
+ ATTR_PACK4(ab, va.va_uid);
+ ab.actual.commonattr |= ATTR_CMN_OWNERID;
+ }
+ if (al.commonattr & ATTR_CMN_GRPID) {
+ ATTR_PACK4(ab, va.va_gid);
+ ab.actual.commonattr |= ATTR_CMN_GRPID;
+ }
+ if (al.commonattr & ATTR_CMN_ACCESSMASK) {
+ ATTR_PACK4(ab, va.va_mode);
+ ab.actual.commonattr |= ATTR_CMN_ACCESSMASK;
+ }
+ if (al.commonattr & ATTR_CMN_FLAGS) {
+ ATTR_PACK4(ab, va.va_flags);
+ ab.actual.commonattr |= ATTR_CMN_FLAGS;
+ }
+ /* We already obtain the user access, so just fill in the buffer here */
+ if (al.commonattr & ATTR_CMN_USERACCESS) {
#if CONFIG_MACF
/*
* Rather than MAC preceding DAC, in this case we want
#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)) {
fsec.fsec_magic = KAUTH_FILESEC_MAGIC;
fsec.fsec_owner = kauth_null_guid;
fsec.fsec_group = kauth_null_guid;
- attrlist_pack_variable2(&ab, &fsec, ((char *)&fsec.fsec_acl - (char *)&fsec), va.va_acl, KAUTH_ACL_COPYSIZE(va.va_acl));
- } else {
+ attrlist_pack_variable2(&ab, &fsec, __offsetof(struct kauth_filesec, fsec_acl), va.va_acl, KAUTH_ACL_COPYSIZE(va.va_acl));
+ ab.actual.commonattr |= ATTR_CMN_EXTENDED_SECURITY;
+ } else if (!return_valid || pack_invalid) {
attrlist_pack_variable(&ab, NULL, 0);
}
}
if (al.commonattr & ATTR_CMN_UUID) {
- if (!VATTR_IS_SUPPORTED(&va, va_uuuid)) {
- ATTR_PACK(&ab, kauth_null_guid);
- } else {
- ATTR_PACK(&ab, va.va_uuuid);
- }
- }
- if (al.commonattr & ATTR_CMN_GRPUUID) {
- if (!VATTR_IS_SUPPORTED(&va, va_guuid)) {
- ATTR_PACK(&ab, kauth_null_guid);
- } else {
- ATTR_PACK(&ab, va.va_guuid);
- }
- }
+ if (VATTR_IS_SUPPORTED(&va, va_uuuid)) {
+ ATTR_PACK(&ab, va.va_uuuid);
+ ab.actual.commonattr |= ATTR_CMN_UUID;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK(&ab, kauth_null_guid);
+ }
+ }
+ if (al.commonattr & ATTR_CMN_GRPUUID) {
+ if (VATTR_IS_SUPPORTED(&va, va_guuid)) {
+ ATTR_PACK(&ab, va.va_guuid);
+ ab.actual.commonattr |= ATTR_CMN_GRPUUID;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK(&ab, kauth_null_guid);
+ }
+ }
if (al.commonattr & ATTR_CMN_FILEID) {
ATTR_PACK8(ab, va.va_fileid);
+ ab.actual.commonattr |= ATTR_CMN_FILEID;
}
if (al.commonattr & ATTR_CMN_PARENTID) {
- uint64_t fileid;
- /*
- * If the filesystem doesn't know the parent ID, we can
- * try to get it via v->v_parent.
- */
- if (VATTR_IS_SUPPORTED(&va, va_parentid)) {
- fileid = va.va_parentid;
- } else {
- struct vnode_attr lva;
- vnode_t pvp;
-
- pvp = vnode_getparent(vp);
-
- if (pvp == NULLVP) {
- error = EINVAL;
- goto out;
- }
- VATTR_INIT(&lva);
- VATTR_WANTED(&lva, va_fileid);
- error = vnode_getattr(pvp, &lva, ctx);
- vnode_put(pvp);
-
- if (error != 0)
- goto out;
- fileid = lva.va_fileid;
- }
- ATTR_PACK8(ab, fileid);
+ ATTR_PACK8(ab, va.va_parentid);
+ ab.actual.commonattr |= ATTR_CMN_PARENTID;
+ }
+
+ if (al.commonattr & ATTR_CMN_FULLPATH) {
+ attrlist_pack_string (&ab, fullpathptr, fullpathlen);
+ ab.actual.commonattr |= ATTR_CMN_FULLPATH;
+ }
+
+ if (al.commonattr & ATTR_CMN_ADDEDTIME) {
+ ATTR_PACK_TIME(ab, va.va_addedtime, proc_is64);
+ ab.actual.commonattr |= ATTR_CMN_ADDEDTIME;
}
- /* directory attributes **************************************************/
- if (vnode_isdir(vp)) {
- if (al.dirattr & ATTR_DIR_LINKCOUNT) /* full count of entries */
+ /* directory attributes *********************************************/
+ if (al.dirattr && (vtype == VDIR)) {
+ if (al.dirattr & ATTR_DIR_LINKCOUNT) { /* full count of entries */
ATTR_PACK4(ab, (uint32_t)va.va_dirlinkcount);
- if (al.dirattr & ATTR_DIR_ENTRYCOUNT)
+ ab.actual.dirattr |= ATTR_DIR_LINKCOUNT;
+ }
+ if (al.dirattr & ATTR_DIR_ENTRYCOUNT) {
ATTR_PACK4(ab, (uint32_t)va.va_nchildren);
- if (al.dirattr & ATTR_DIR_MOUNTSTATUS)
- ATTR_PACK_CAST(&ab, uint32_t, (vp->v_flag & VROOT) ? DIR_MNTSTATUS_MNTPOINT : 0);
+ ab.actual.dirattr |= ATTR_DIR_ENTRYCOUNT;
+ }
+ if (al.dirattr & ATTR_DIR_MOUNTSTATUS) {
+ uint32_t mntstat;
+
+ mntstat = (vp->v_flag & VROOT) ? DIR_MNTSTATUS_MNTPOINT : 0;
+#if CONFIG_TRIGGERS
+ /*
+ * Report back on active vnode triggers
+ * that can directly trigger a mount
+ */
+ if (vp->v_resolve &&
+ !(vp->v_resolve->vr_flags & VNT_NO_DIRECT_MOUNT)) {
+ mntstat |= DIR_MNTSTATUS_TRIGGER;
+ }
+#endif
+ ATTR_PACK4(ab, mntstat);
+ ab.actual.dirattr |= ATTR_DIR_MOUNTSTATUS;
+ }
}
/* file attributes **************************************************/
- if (!vnode_isdir(vp)) {
- if (al.fileattr & ATTR_FILE_LINKCOUNT)
+ if (al.fileattr && (vtype != VDIR)) {
+
+ size_t rsize = 0;
+ uint64_t rlength = 0;
+ uint64_t ralloc = 0;
+ /*
+ * Pre-fetch the rsrc attributes now so we only get them once.
+ * Fetch the resource fork size/allocation via xattr interface
+ */
+ if (al.fileattr & (ATTR_FILE_TOTALSIZE | ATTR_FILE_ALLOCSIZE | ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE)) {
+ if ((error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &rsize, XATTR_NOSECURITY, ctx)) != 0) {
+ if ((error == ENOENT) || (error == ENOATTR) || (error == ENOTSUP) || (error == EPERM)|| (error == EACCES)) {
+ rsize = 0;
+ error = 0;
+ } else {
+ goto out;
+ }
+ }
+ rlength = rsize;
+
+ if (al.fileattr & (ATTR_FILE_RSRCALLOCSIZE | ATTR_FILE_ALLOCSIZE)) {
+ uint32_t blksize = vp->v_mount->mnt_vfsstat.f_bsize;
+ if (blksize == 0) {
+ blksize = 512;
+ }
+ ralloc = roundup(rsize, blksize);
+ }
+ }
+
+ if (al.fileattr & ATTR_FILE_LINKCOUNT) {
ATTR_PACK4(ab, (uint32_t)va.va_nlink);
- if (al.fileattr & ATTR_FILE_TOTALSIZE)
- ATTR_PACK8(ab, va.va_total_size);
- if (al.fileattr & ATTR_FILE_ALLOCSIZE)
- ATTR_PACK8(ab, va.va_total_alloc);
- if (al.fileattr & ATTR_FILE_IOBLOCKSIZE)
+ ab.actual.fileattr |= ATTR_FILE_LINKCOUNT;
+ }
+ /*
+ * Note the following caveats for the TOTALSIZE and ALLOCSIZE attributes:
+ * We infer that if the filesystem does not support va_data_size or va_data_alloc
+ * it must not know about alternate forks. So when we need to gather
+ * the total size or total alloc, it's OK to substitute the total size for
+ * the data size below. This is because it is likely a flat filesystem and we must
+ * be using AD files to store the rsrc fork and EAs.
+ *
+ * Additionally, note that getattrlist is barred from being called on
+ * resource fork paths. (Search for CN_ALLOWRSRCFORK). So if the filesystem does
+ * support va_data_size, it is guaranteed to represent the data fork's size. This
+ * is an important distinction to make because when we call vnode_getattr on
+ * an HFS resource fork vnode, to get the size, it will vend out the resource
+ * fork's size (it only gets the size of the passed-in vnode).
+ */
+ if (al.fileattr & ATTR_FILE_TOTALSIZE) {
+ uint64_t totalsize = rlength;
+
+ if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
+ totalsize += va.va_data_size;
+ } else {
+ totalsize += va.va_total_size;
+ }
+
+ ATTR_PACK8(ab, totalsize);
+ ab.actual.fileattr |= ATTR_FILE_TOTALSIZE;
+ }
+ if (al.fileattr & ATTR_FILE_ALLOCSIZE) {
+ uint64_t totalalloc = ralloc;
+
+ /*
+ * If data_alloc is supported, then it must represent the
+ * data fork size.
+ */
+ if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
+ totalalloc += va.va_data_alloc;
+ }
+ else {
+ totalalloc += va.va_total_alloc;
+ }
+
+ ATTR_PACK8(ab, totalalloc);
+ ab.actual.fileattr |= ATTR_FILE_ALLOCSIZE;
+ }
+ if (al.fileattr & ATTR_FILE_IOBLOCKSIZE) {
ATTR_PACK4(ab, va.va_iosize);
- if (al.fileattr & ATTR_FILE_CLUMPSIZE)
- ATTR_PACK4(ab, 0); /* XXX value is deprecated */
+ ab.actual.fileattr |= ATTR_FILE_IOBLOCKSIZE;
+ }
+ if (al.fileattr & ATTR_FILE_CLUMPSIZE) {
+ if (!return_valid || pack_invalid) {
+ ATTR_PACK4(ab, 0); /* this value is deprecated */
+ ab.actual.fileattr |= ATTR_FILE_CLUMPSIZE;
+ }
+ }
if (al.fileattr & ATTR_FILE_DEVTYPE) {
+ uint32_t dev;
+
if ((vp->v_type == VCHR) || (vp->v_type == VBLK)) {
- ATTR_PACK(&ab, vp->v_specinfo->si_rdev);
+ if (vp->v_specinfo != NULL)
+ dev = vp->v_specinfo->si_rdev;
+ else
+ dev = va.va_rdev;
} else {
- ATTR_PACK_CAST(&ab, uint32_t, 0);
+ dev = 0;
}
+ ATTR_PACK4(ab, dev);
+ ab.actual.fileattr |= ATTR_FILE_DEVTYPE;
}
+
+ /*
+ * If the filesystem does not support datalength
+ * or dataallocsize, then we infer that totalsize and
+ * totalalloc are substitutes.
+ */
if (al.fileattr & ATTR_FILE_DATALENGTH) {
if (VATTR_IS_SUPPORTED(&va, va_data_size)) {
ATTR_PACK8(ab, va.va_data_size);
} else {
ATTR_PACK8(ab, va.va_total_size);
}
+ ab.actual.fileattr |= ATTR_FILE_DATALENGTH;
}
if (al.fileattr & ATTR_FILE_DATAALLOCSIZE) {
if (VATTR_IS_SUPPORTED(&va, va_data_alloc)) {
} else {
ATTR_PACK8(ab, va.va_total_alloc);
}
+ ab.actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
}
- /* fetch resource fork size/allocation via xattr interface */
- if (al.fileattr & (ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE)) {
- size_t rsize;
- uint64_t rlength;
-
- if ((error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL, &rsize, XATTR_NOSECURITY, ctx)) != 0) {
- if ((error == ENOENT) || (error == ENOATTR) || (error == ENOTSUP) || (error == EPERM)) {
- rsize = 0;
- error = 0;
- } else {
- goto out;
- }
- }
- if (al.fileattr & ATTR_FILE_RSRCLENGTH) {
- rlength = rsize;
- ATTR_PACK8(ab, rlength);
- }
- if (al.fileattr & ATTR_FILE_RSRCALLOCSIZE) {
- uint32_t blksize = vp->v_mount->mnt_vfsstat.f_bsize;
- if (blksize == 0)
- blksize = 512;
- rlength = roundup(rsize, blksize);
- ATTR_PACK8(ab, rlength);
- }
+ /* already got the resource fork size/allocation above */
+ if (al.fileattr & ATTR_FILE_RSRCLENGTH) {
+ ATTR_PACK8(ab, rlength);
+ ab.actual.fileattr |= ATTR_FILE_RSRCLENGTH;
+ }
+ if (al.fileattr & ATTR_FILE_RSRCALLOCSIZE) {
+ ATTR_PACK8(ab, ralloc);
+ ab.actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE;
}
}
-
+
/* diagnostic */
- if ((ab.fixedcursor - ab.base) != fixedsize)
- panic("packed field size mismatch; allocated %ld but packed %d for common %08x vol %08x",
- fixedsize, ab.fixedcursor - ab.base, al.commonattr, al.volattr);
- if (ab.varcursor != (ab.base + ab.needed))
- panic("packed variable field size mismatch; used %d but expected %ld", ab.varcursor - ab.base, ab.needed);
+ if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize)
+ panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
+ fixedsize, (long) (ab.fixedcursor - ab.base), al.commonattr, al.volattr);
+ if (!return_valid && ab.varcursor != (ab.base + ab.needed))
+ panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab.varcursor - ab.base), ab.needed);
/*
* In the compatible case, we report the smaller of the required and returned sizes.
* they gave us, so they can always check for truncation themselves.
*/
*(uint32_t *)ab.base = (uap->options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
+
+ /* Return attribute set output if requested. */
+ if (return_valid) {
+ ab.actual.commonattr |= ATTR_CMN_RETURNED_ATTRS;
+ if (pack_invalid) {
+ /* Only report the attributes that are valid */
+ ab.actual.commonattr &= ab.valid.commonattr;
+ ab.actual.dirattr &= ab.valid.dirattr;
+ ab.actual.fileattr &= ab.valid.fileattr;
+ }
+ bcopy(&ab.actual, ab.base + sizeof(uint32_t), sizeof (ab.actual));
+ }
/* Only actually copyout as much out as the user buffer can hold */
error = copyout(ab.base, uap->attributeBuffer, imin(uap->bufferSize, ab.allocated));
out:
if (va.va_name)
kfree(va.va_name, MAXPATHLEN);
+ if (fullpathptr)
+ kfree(fullpathptr, MAXPATHLEN);
if (vname)
- vnode_putname(vname);
- if (vp)
- vnode_put(vp);
+ vnode_putname(vname);
if (ab.base != NULL)
FREE(ab.base, M_TEMP);
if (VATTR_IS_SUPPORTED(&va, va_acl) && (va.va_acl != NULL))
return(error);
}
+int
+fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval)
+{
+ struct vfs_context *ctx;
+ vnode_t vp = NULL;
+ int error;
+ struct getattrlist_args ap;
+
+ ctx = vfs_context_current();
+ error = 0;
+
+ if ((error = file_vnode(uap->fd, &vp)) != 0)
+ return (error);
+
+ if ((error = vnode_getwithref(vp)) != 0) {
+ file_drop(uap->fd);
+ return(error);
+ }
+
+ ap.path = 0;
+ ap.alist = uap->alist;
+ ap.attributeBuffer = uap->attributeBuffer;
+ ap.bufferSize = uap->bufferSize;
+ ap.options = uap->options;
+
+ /* Default to using the vnode's name. */
+ error = getattrlist_internal(vp, &ap, NULL, p, ctx);
+
+ file_drop(uap->fd);
+ if (vp)
+ vnode_put(vp);
+
+ return error;
+}
+
+int
+getattrlist(proc_t p, struct getattrlist_args *uap, __unused int32_t *retval)
+{
+ struct vfs_context *ctx;
+ struct nameidata nd;
+ vnode_t vp = NULL;
+ u_long nameiflags;
+ int error;
+
+ ctx = vfs_context_current();
+ error = 0;
+
+ /*
+ * Look up the file.
+ */
+ nameiflags = NOTRIGGER | AUDITVNPATH1;
+ if (!(uap->options & FSOPT_NOFOLLOW))
+ nameiflags |= FOLLOW;
+ NDINIT(&nd, LOOKUP, OP_GETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
+
+ if ((error = namei(&nd)) != 0) {
+ /* vp is still uninitialized */
+ return error;
+ }
+
+ vp = nd.ni_vp;
+ /* Pass along our componentname to getattrlist_internal */
+ error = getattrlist_internal(vp, uap, &(nd.ni_cnd), p, ctx);
+
+ /* Retain the namei reference until the getattrlist completes. */
+ nameidone(&nd);
+ if (vp)
+ vnode_put(vp);
+
+ return error;
+}
+
static int
attrlist_unpack_fixed(char **cursor, char *end, void *buf, ssize_t size)
{
#define ATTR_UNPACK_TIME(v, is64) \
do { \
if (is64) { \
- struct user_timespec us; \
+ struct user64_timespec us; \
ATTR_UNPACK(us); \
v.tv_sec = us.tv_sec; \
v.tv_nsec = us.tv_nsec; \
} else { \
- ATTR_UNPACK(v); \
+ struct user32_timespec us; \
+ ATTR_UNPACK(us); \
+ v.tv_sec = us.tv_sec; \
+ v.tv_nsec = us.tv_nsec; \
} \
} while(0)
/*
* Write attributes.
*/
-int
-setattrlist(proc_t p, struct setattrlist_args *uap, __unused register_t *retval)
+static int
+setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_context_t ctx)
{
struct attrlist al;
- struct vfs_context context, *ctx;
struct vnode_attr va;
struct attrreference ar;
- struct nameidata nd;
- vnode_t vp;
- u_long nameiflags;
kauth_action_t action;
char *user_buf, *cursor, *bufend, *fndrinfo, *cp, *volname;
int proc_is64, error;
uint32_t nace;
kauth_filesec_t rfsec;
- context.vc_thread = current_thread();
- context.vc_ucred = kauth_cred_get();
- ctx = &context;
- vp = NULL;
user_buf = NULL;
fndrinfo = NULL;
volname = NULL;
proc_is64 = proc_is64bit(p);
VATTR_INIT(&va);
-
- /*
- * Look up the file.
- */
- nameiflags = 0;
- if ((uap->options & FSOPT_NOFOLLOW) == 0)
- nameiflags |= FOLLOW;
- NDINIT(&nd, LOOKUP, nameiflags | AUDITVNPATH1, UIO_USERSPACE, uap->path, &context);
- if ((error = namei(&nd)) != 0)
- goto out;
- vp = nd.ni_vp;
- nameidone(&nd);
-
/*
* Fetch the attribute set and validate.
*/
}
}
+ /*
+ * If the caller's bitmaps indicate that there are no attributes to set,
+ * then exit early. In particular, we want to avoid the MALLOC below
+ * since the caller's bufferSize could be zero, and MALLOC of zero bytes
+ * returns a NULL pointer, which would cause setattrlist to return ENOMEM.
+ */
+ if (al.commonattr == 0 &&
+ (al.volattr & ~ATTR_VOL_INFO) == 0 &&
+ al.dirattr == 0 &&
+ al.fileattr == 0 &&
+ al.forkattr == 0) {
+ error = 0;
+ goto out;
+ }
+
/*
* Make the naive assumption that the caller has supplied a reasonable buffer
* size. We could be more careful by pulling in the fixed-size region, checking
VFS_DEBUG(ctx, vp, "ATTRLIST - copied in %d bytes of user attributes to %p", uap->bufferSize, user_buf);
#if CONFIG_MACF
- error = mac_vnode_check_setattrlist(&context, vp, &al);
+ error = mac_vnode_check_setattrlist(ctx, vp, &al);
if (error)
goto out;
#endif /* MAC */
*/
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 + 1) > bufend) || /* no space for acl */
+ if (((((char *)rfsec) + KAUTH_FILESEC_SIZE(0)) > bufend) || /* no space for acl */
(rfsec->fsec_magic != KAUTH_FILESEC_MAGIC) || /* bad magic */
(KAUTH_FILESEC_COPYSIZE(rfsec) != ar.attr_length) || /* size does not match */
((cp + KAUTH_FILESEC_COPYSIZE(rfsec)) > bufend)) { /* ACEs overrun buffer */
if (al.volattr & ATTR_VOL_INFO) {
if (al.volattr & ATTR_VOL_NAME) {
volname = cursor;
- ATTR_UNPACK(ar);
+ ATTR_UNPACK(ar);
+ /* attr_length cannot be 0! */
+ if ((ar.attr_dataoffset < 0) || (ar.attr_length == 0)) {
+ VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied (2) ", ar.attr_dataoffset);
+ error = EINVAL;
+ goto out;
+ }
+
volname += ar.attr_dataoffset;
if ((volname + ar.attr_length) > bufend) {
error = EINVAL;
* Validate and authorize.
*/
action = 0;
- if ((va.va_active != 0LL) && ((error = vnode_authattr(vp, &va, &action, &context)) != 0)) {
+ if ((va.va_active != 0LL) && ((error = vnode_authattr(vp, &va, &action, ctx)) != 0)) {
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: attribute changes refused: %d", error);
goto out;
}
goto out;
}
} else {
- action |= KAUTH_VNODE_WRITE_ATTRIBUTES;
+ action |= KAUTH_VNODE_WRITE_EXTATTRIBUTES;
}
}
- if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, &context)) != 0)) {
+ if ((action != 0) && ((error = vnode_authorize(vp, NULL, action, ctx)) != 0)) {
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: authorization failed");
goto out;
}
/*
* Write the attributes if we have any.
*/
- if ((va.va_active != 0LL) && ((error = vnode_setattr(vp, &va, &context)) != 0)) {
+ if ((va.va_active != 0LL) && ((error = vnode_setattr(vp, &va, ctx)) != 0)) {
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error);
goto out;
}
if (fndrinfo != NULL) {
if (al.volattr & ATTR_VOL_INFO) {
if (vp->v_tag == VT_HFS) {
- error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, &context);
+ error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, ctx);
if (error != 0)
goto out;
} else {
/* all done and successful */
out:
- if (vp != NULL)
- vnode_put(vp);
if (user_buf != NULL)
FREE(user_buf, M_TEMP);
VFS_DEBUG(ctx, vp, "ATTRLIST - set returning %d", error);
return(error);
}
+
+int
+setattrlist(proc_t p, struct setattrlist_args *uap, __unused int32_t *retval)
+{
+ struct vfs_context *ctx;
+ struct nameidata nd;
+ vnode_t vp = NULL;
+ u_long nameiflags;
+ int error = 0;
+
+ ctx = vfs_context_current();
+
+ /*
+ * Look up the file.
+ */
+ nameiflags = AUDITVNPATH1;
+ if ((uap->options & FSOPT_NOFOLLOW) == 0)
+ nameiflags |= FOLLOW;
+ NDINIT(&nd, LOOKUP, OP_SETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
+ if ((error = namei(&nd)) != 0)
+ goto out;
+ vp = nd.ni_vp;
+ nameidone(&nd);
+
+ error = setattrlist_internal(vp, uap, p, ctx);
+out:
+ if (vp != NULL)
+ vnode_put(vp);
+ return error;
+}
+
+int
+fsetattrlist(proc_t p, struct fsetattrlist_args *uap, __unused int32_t *retval)
+{
+ struct vfs_context *ctx;
+ vnode_t vp = NULL;
+ int error;
+ struct setattrlist_args ap;
+
+ ctx = vfs_context_current();
+
+ if ((error = file_vnode(uap->fd, &vp)) != 0)
+ return (error);
+
+ if ((error = vnode_getwithref(vp)) != 0) {
+ file_drop(uap->fd);
+ return(error);
+ }
+
+ ap.path = 0;
+ ap.alist = uap->alist;
+ ap.attributeBuffer = uap->attributeBuffer;
+ ap.bufferSize = uap->bufferSize;
+ ap.options = uap->options;
+
+ error = setattrlist_internal(vp, &ap, p, ctx);
+ file_drop(uap->fd);
+ if (vp != NULL)
+ vnode_put(vp);
+
+ return error;
+}
+