/*
- * Copyright (c) 1995-2016 Apple Inc. All rights reserved.
+ * Copyright (c) 1995-2018 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <sys/fsevents.h>
#include <kern/kalloc.h>
#include <miscfs/specfs/specdev.h>
+#include <security/audit/audit.h>
#if CONFIG_MACF
#include <security/mac_framework.h>
#define ATTR_PACK8(AB, V) \
do { \
if ((AB.allocated - (AB.fixedcursor - AB.base)) >= 8) { \
- *(uint64_t *)AB.fixedcursor = *(uint64_t *)&V; \
+ memcpy(AB.fixedcursor, &V, 8); \
AB.fixedcursor += 8; \
} \
} while (0)
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_SIZE, VFSATTR_BIT(f_blocks) | VFSATTR_BIT(f_bsize), 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_ENCODINGSUSED, 0, sizeof(uint64_t)},
{ATTR_VOL_CAPABILITIES, VFSATTR_BIT(f_capabilities), sizeof(vol_capabilities_attr_t)},
{ATTR_VOL_UUID, VFSATTR_BIT(f_uuid), sizeof(uuid_t)},
+ {ATTR_VOL_QUOTA_SIZE, VFSATTR_BIT(f_quota) | VFSATTR_BIT(f_bsize), sizeof(off_t)},
+ {ATTR_VOL_RESERVED_SIZE, VFSATTR_BIT(f_reserved) | VFSATTR_BIT(f_bsize), sizeof(off_t)},
{ATTR_VOL_ATTRIBUTES, VFSATTR_BIT(f_attributes), sizeof(vol_attributes_attr_t)},
{ATTR_VOL_INFO, 0, 0},
{0, 0, 0}
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;
*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)
getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t *sizep, int is_64bit)
{
int error;
+ if (!alp)
+ return EINVAL;
/*
* Parse the above tables.
return (EINVAL);
}
if ((error = getvolattrlist_parsetab(getvolattrlist_common_tab,
- alp->commonattr, vsp, sizep,
- is_64bit)) != 0) {
+ alp->commonattr, vsp, sizep,
+ is_64bit,
+ sizeof(getvolattrlist_common_tab)/sizeof(getvolattrlist_common_tab[0]))) != 0) {
return(error);
}
}
if (alp->volattr &&
- (error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit)) != 0)
+ (error = getvolattrlist_parsetab(getvolattrlist_vol_tab, alp->volattr, vsp, sizep, is_64bit, sizeof(getvolattrlist_vol_tab)/sizeof(getvolattrlist_vol_tab[0]))) != 0)
return(error);
return(0);
{ATTR_DIR_LINKCOUNT, VATTR_BIT(va_dirlinkcount), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
{ATTR_DIR_ENTRYCOUNT, VATTR_BIT(va_nchildren), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
{ATTR_DIR_MOUNTSTATUS, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
+ {ATTR_DIR_ALLOCSIZE, VATTR_BIT(va_total_alloc) | VATTR_BIT(va_total_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
+ {ATTR_DIR_IOBLOCKSIZE, VATTR_BIT(va_iosize), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES},
+ {ATTR_DIR_DATALENGTH, VATTR_BIT(va_total_size) | VATTR_BIT(va_data_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
{0, 0, 0, 0}
};
static struct getattrlist_attrtab getattrlist_file_tab[] = {
{ATTR_FILE_RSRCLENGTH, 0, sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
{ATTR_FILE_RSRCALLOCSIZE, 0, sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
{0, 0, 0, 0}
-};
+};
+
+//for forkattr bits repurposed as new common attributes
+static struct getattrlist_attrtab getattrlist_common_extended_tab[] = {
+ {ATTR_CMNEXT_RELPATH, 0, sizeof(struct attrreference), KAUTH_VNODE_READ_ATTRIBUTES},
+ {ATTR_CMNEXT_PRIVATESIZE, VATTR_BIT(va_private_size), sizeof(off_t), KAUTH_VNODE_READ_ATTRIBUTES},
+ {ATTR_CMNEXT_LINKID, VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(uint64_t), KAUTH_VNODE_READ_ATTRIBUTES},
+ {0, 0, 0, 0}
+};
/*
* This table is for attributes which are only set from the getattrlistbulk(2)
{0, 0, 0, 0}
};
+static struct getattrlist_attrtab getattrlistbulk_common_extended_tab[] = {
+ /* getattrlist_parsetab() expects > 1 entries */
+ {0, 0, 0, 0},
+ {0, 0, 0, 0}
+};
+
/*
* The following are attributes that VFS can derive.
*
* A majority of them are the same attributes that are required for stat(2) and statfs(2).
*/
#define VFS_DFLT_ATTR_VOL (ATTR_VOL_FSTYPE | ATTR_VOL_SIGNATURE | \
- ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE | \
+ ATTR_VOL_SIZE | ATTR_VOL_SPACEFREE | ATTR_VOL_QUOTA_SIZE | ATTR_VOL_RESERVED_SIZE | \
ATTR_VOL_SPACEAVAIL | ATTR_VOL_MINALLOCATION | \
ATTR_VOL_ALLOCATIONCLUMP | ATTR_VOL_IOBLOCKSIZE | \
ATTR_VOL_MOUNTPOINT | ATTR_VOL_MOUNTFLAGS | \
ATTR_CMN_DOCUMENT_ID | ATTR_CMN_GEN_COUNT | \
ATTR_CMN_DATA_PROTECT_FLAGS)
-#define VFS_DFLT_ATT_CMN_EXT (ATTR_CMN_EXT_GEN_COUNT | ATTR_CMN_EXT_DOCUMENT_ID |\
- ATTR_CMN_EXT_DATA_PROTECT_FLAGS)
+#define VFS_DFLT_ATTR_CMN_EXT (ATTR_CMNEXT_PRIVATESIZE | ATTR_CMNEXT_LINKID)
#define VFS_DFLT_ATTR_DIR (ATTR_DIR_LINKCOUNT | ATTR_DIR_MOUNTSTATUS)
static int
getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs,
struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp,
- int is_64bit)
+ int is_64bit, unsigned int maxiter)
{
attrgroup_t recognised;
-
recognised = 0;
+ if (!tab)
+ return EINVAL;
+
do {
/* is this attribute set? */
if (tab->attr & attrs) {
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)
* 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;
*sizep = sizeof(uint32_t); /* length count */
*actionp = 0;
if (alp->commonattr &&
- (error = getattrlist_parsetab(getattrlist_common_tab, alp->commonattr, vap, sizep, actionp, is_64bit)) != 0)
+ (error = getattrlist_parsetab(getattrlist_common_tab, alp->commonattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_common_tab)/sizeof(getattrlist_common_tab[0]))) != 0)
return(error);
if (isdir && alp->dirattr &&
- (error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr, vap, sizep, actionp, is_64bit)) != 0)
+ (error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_dir_tab)/sizeof(getattrlist_dir_tab[0]))) != 0)
return(error);
if (!isdir && alp->fileattr &&
- (error = getattrlist_parsetab(getattrlist_file_tab, alp->fileattr, vap, sizep, actionp, is_64bit)) != 0)
+ (error = getattrlist_parsetab(getattrlist_file_tab, alp->fileattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_file_tab)/sizeof(getattrlist_file_tab[0]))) != 0)
+ return(error);
+ if (use_fork && alp->forkattr &&
+ (error = getattrlist_parsetab(getattrlist_common_extended_tab, alp->forkattr, vap, sizep, actionp, is_64bit, sizeof(getattrlist_common_extended_tab)/sizeof(getattrlist_common_extended_tab[0]))) != 0)
return(error);
return(0);
*/
static int
getattrlist_setupvattr_all(struct attrlist *alp, struct vnode_attr *vap,
- enum vtype obj_type, ssize_t *fixedsize, int is_64bit)
+ enum vtype obj_type, ssize_t *fixedsize, int is_64bit, int use_fork)
{
int error = 0;
}
if (alp->commonattr) {
error = getattrlist_parsetab(getattrlist_common_tab,
- alp->commonattr, vap, fixedsize, NULL, is_64bit);
+ alp->commonattr, vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlist_common_tab)/sizeof(getattrlist_common_tab[0]));
if (!error) {
/* Ignore any errrors from the bulk table */
(void)getattrlist_parsetab(getattrlistbulk_common_tab,
- alp->commonattr, vap, fixedsize, NULL, is_64bit);
+ alp->commonattr, vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlistbulk_common_tab)/sizeof(getattrlistbulk_common_tab[0]));
/*
* turn off va_fsid since we will be using only
* va_fsid64 for ATTR_CMN_FSID.
if (!error && (obj_type == VNON || obj_type == VDIR) && alp->dirattr) {
error = getattrlist_parsetab(getattrlist_dir_tab, alp->dirattr,
- vap, fixedsize, NULL, is_64bit);
+ vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlist_dir_tab)/sizeof(getattrlist_dir_tab[0]));
}
if (!error && (obj_type != VDIR) && alp->fileattr) {
error = getattrlist_parsetab(getattrlist_file_tab,
- alp->fileattr, vap, fixedsize, NULL, is_64bit);
+ alp->fileattr, vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlist_file_tab)/sizeof(getattrlist_file_tab[0]));
if (!error) {
/*Ignore any errors from the bulk table */
(void)getattrlist_parsetab(getattrlistbulk_file_tab,
- alp->fileattr, vap, fixedsize, NULL, is_64bit);
+ alp->fileattr, vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlistbulk_file_tab)/sizeof(getattrlistbulk_file_tab[0]));
+ }
+ }
+
+ /* fork attributes are like extended common attributes if enabled*/
+ if (!error && use_fork && alp->forkattr) {
+ error = getattrlist_parsetab(getattrlist_common_extended_tab,
+ alp->forkattr, vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlist_common_extended_tab)/sizeof(getattrlist_common_extended_tab[0]));
+
+ if (!error) {
+ (void)getattrlist_parsetab(getattrlistbulk_common_extended_tab,
+ alp->forkattr, vap, fixedsize, NULL, is_64bit,
+ sizeof(getattrlistbulk_common_extended_tab)/sizeof(getattrlistbulk_common_extended_tab[0]));
}
}
vfs_setup_vattr_from_attrlist(struct attrlist *alp, struct vnode_attr *vap,
enum vtype obj_vtype, ssize_t *attrs_fixed_sizep, vfs_context_t ctx)
{
+ // the caller passes us no options, we assume the caller wants the new fork
+ // attr behavior, hence the hardcoded 1
return (getattrlist_setupvattr_all(alp, vap, obj_vtype,
- attrs_fixed_sizep, IS_64BIT_PROCESS(vfs_context_proc(ctx))));
+ attrs_fixed_sizep, IS_64BIT_PROCESS(vfs_context_proc(ctx)), 1));
}
* 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;
}
} 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
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;
* Note that since we won't ever copy out more than the caller requested,
* we never need to allocate more than they offer.
*/
- ab.allocated = ulmin(bufferSize, fixedsize + varsize);
- if (ab.allocated > ATTR_MAX_BUFFER) {
+ ab.allocated = fixedsize + varsize;
+ if (((size_t)ab.allocated) > ATTR_MAX_BUFFER) {
error = ENOMEM;
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
goto out;
}
+
+ 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;
+ }
+
MALLOC(ab.base, char *, ab.allocated, M_TEMP, M_ZERO | M_WAITOK);
if (ab.base == NULL) {
error = ENOMEM;
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;
vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] &= ~VOL_CAP_INT_EXTENDED_SECURITY;
}
vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY;
+
+ /*
+ * if the filesystem doesn't mark either VOL_CAP_FMT_NO_IMMUTABLE_FILES
+ * or VOL_CAP_FMT_NO_PERMISSIONS as valid, assume they're not supported
+ */
+ if (!(vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_NO_IMMUTABLE_FILES)) {
+ vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~VOL_CAP_FMT_NO_IMMUTABLE_FILES;
+ vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_IMMUTABLE_FILES;
+ }
+
+ if (!(vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_NO_PERMISSIONS)) {
+ vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~VOL_CAP_FMT_NO_PERMISSIONS;
+ vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_PERMISSIONS;
+ }
+
ATTR_PACK(&ab, vs.f_capabilities);
ab.actual.volattr |= ATTR_VOL_CAPABILITIES;
}
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 */
* of the result buffer, even if we copied less out. The caller knows how big a buffer
* they gave us, so they can always check for truncation themselves.
*/
- *(uint32_t *)ab.base = (options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed);
-
+ *(uint32_t *)ab.base = (options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(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 */
if (UIO_SEG_IS_USER_SPACE(segflg))
error = copyout(ab.base, CAST_USER_ADDR_T(attributeBuffer),
- ab.allocated);
+ ulmin(bufferSize, ab.needed));
else
- bcopy(ab.base, (void *)attributeBuffer, (size_t)ab.allocated);
+ bcopy(ab.base, (void *)attributeBuffer, (size_t)ulmin(bufferSize, ab.needed));
out:
if (vs.f_vol_name != NULL)
static errno_t
attr_pack_dir(struct vnode *vp, struct attrlist *alp, struct _attrlist_buf *abp,
- struct vnode_attr *vap)
+ struct vnode_attr *vap, int return_valid, int pack_invalid)
{
if (alp->dirattr & ATTR_DIR_LINKCOUNT) { /* full count of entries */
ATTR_PACK4((*abp), (uint32_t)vap->va_dirlinkcount);
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;
}
if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
totalsize += vap->va_data_size;
- } else {
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
totalsize += vap->va_total_size;
}
*/
if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
totalalloc += vap->va_data_alloc;
- } else {
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
totalalloc += vap->va_total_alloc;
}
}
}
if (alp->fileattr & ATTR_FILE_IOBLOCKSIZE) {
- ATTR_PACK4((*abp), vap->va_iosize);
- abp->actual.fileattr |= ATTR_FILE_IOBLOCKSIZE;
+ if (VATTR_IS_SUPPORTED(vap, va_iosize)) {
+ ATTR_PACK4((*abp), vap->va_iosize);
+ abp->actual.fileattr |= ATTR_FILE_IOBLOCKSIZE;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
}
if (alp->fileattr & ATTR_FILE_CLUMPSIZE) {
if (!return_valid || pack_invalid) {
if (alp->fileattr & ATTR_FILE_DATALENGTH) {
if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
ATTR_PACK8((*abp), vap->va_data_size);
- } else {
+ abp->actual.fileattr |= ATTR_FILE_DATALENGTH;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_size)){
ATTR_PACK8((*abp), vap->va_total_size);
+ abp->actual.fileattr |= ATTR_FILE_DATALENGTH;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
}
- abp->actual.fileattr |= ATTR_FILE_DATALENGTH;
}
if (alp->fileattr & ATTR_FILE_DATAALLOCSIZE) {
if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
ATTR_PACK8((*abp), vap->va_data_alloc);
- } else {
+ abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)){
ATTR_PACK8((*abp), vap->va_total_alloc);
+ abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
}
- abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
}
/* already got the resource fork size/allocation above */
if (alp->fileattr & ATTR_FILE_RSRCLENGTH) {
return (error);
}
+/*
+ * Pack FORKATTR attributes into a user buffer.
+ * alp is a pointer to the bitmap of attributes required.
+ * abp is the state of the attribute filling operation.
+ * The attribute data (along with some other fields that are required
+ * are in ad.
+ */
+static errno_t
+attr_pack_common_extended(struct vnode *vp, struct attrlist *alp,
+ struct _attrlist_buf *abp, const char *relpathptr, ssize_t relpathlen,
+ struct vnode_attr *vap, int return_valid, int pack_invalid)
+{
+ if (vp && (alp->forkattr & ATTR_CMNEXT_RELPATH)) {
+ attrlist_pack_string(abp, relpathptr, relpathlen);
+ abp->actual.forkattr |= ATTR_CMNEXT_RELPATH;
+ }
+
+ if (alp->forkattr & ATTR_CMNEXT_PRIVATESIZE) {
+ if (VATTR_IS_SUPPORTED(vap, va_private_size)) {
+ ATTR_PACK8((*abp), vap->va_private_size);
+ abp->actual.forkattr |= ATTR_CMNEXT_PRIVATESIZE;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+
+ if (alp->forkattr & ATTR_CMNEXT_LINKID) {
+ uint64_t linkid;
+
+ if (VATTR_IS_SUPPORTED(vap, va_linkid))
+ linkid = vap->va_linkid;
+ else
+ linkid = vap->va_fileid;
+
+ ATTR_PACK8((*abp), linkid);
+ abp->actual.forkattr |= ATTR_CMNEXT_LINKID;
+ }
+
+ return 0;
+}
+
static void
vattr_get_alt_data(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
int return_valid, int is_bulk, vfs_context_t ctx)
static errno_t
calc_varsize(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
ssize_t *varsizep, char *fullpathptr, ssize_t *fullpathlenp,
- const char **vnamep, const char **cnpp, ssize_t *cnlp)
+ char *relpathptr, ssize_t *relpathlenp, const char **vnamep,
+ const char **cnpp, ssize_t *cnlp)
{
int error = 0;
*varsizep += roundup(((*fullpathlenp) + 1), 4);
}
+ /*
+ * Compute this vnode's volume relative path.
+ */
+ if (vp && (alp->forkattr & ATTR_CMNEXT_RELPATH)) {
+ int len;
+ int err;
+
+ /* call build_path making sure NOT to use the cache-only behavior */
+ err = build_path(vp, relpathptr, MAXPATHLEN, &len, BUILDPATH_VOLUME_RELATIVE, vfs_context_current());
+ if (err) {
+ error = err;
+ goto out;
+ }
+
+ //`len' includes trailing null
+ *relpathlenp = len - 1;
+ *varsizep += roundup(len, 4);
+ }
+
/*
* We have a kauth_acl_t but we will be returning a kauth_filesec_t.
*
ssize_t cnl;
char *fullpathptr;
ssize_t fullpathlen;
+ char *relpathptr;
+ ssize_t relpathlen;
int error;
int proc_is64;
int return_valid;
int pack_invalid;
int alloc_local_buf;
+ const int use_fork = options & FSOPT_ATTR_CMN_EXTENDED;
proc_is64 = proc_is64bit(vfs_context_proc(ctx));
ab.base = NULL;
cnl = 0;
fullpathptr = NULL;
fullpathlen = 0;
+ relpathptr = NULL;
+ relpathlen = 0;
error = 0;
alloc_local_buf = 0;
if (!VATTR_ALL_SUPPORTED(vap)) {
if (return_valid && pack_invalid) {
/* Fix up valid mask for post processing */
- getattrlist_fixupattrs(&ab.valid, vap);
+ getattrlist_fixupattrs(&ab.valid, vap, use_fork);
/* Force packing of everything asked for */
vap->va_supported = vap->va_active;
} else if (return_valid) {
/* Adjust the requested attributes */
getattrlist_fixupattrs(
- (attribute_set_t *)&(alp->commonattr), vap);
+ (attribute_set_t *)&(alp->commonattr), vap, use_fork);
} else {
error = EINVAL;
}
goto out;
}
+ //if a path is requested, allocate a temporary buffer to build it
if (vp && (alp->commonattr & (ATTR_CMN_FULLPATH))) {
fullpathptr = (char*) kalloc(MAXPATHLEN);
if (fullpathptr == NULL) {
bzero(fullpathptr, MAXPATHLEN);
}
+ // only interpret fork attributes if they're used as new common attributes
+ if (vp && use_fork && (alp->forkattr & (ATTR_CMNEXT_RELPATH))) {
+ relpathptr = (char*) kalloc(MAXPATHLEN);
+ if (relpathptr == NULL) {
+ error = ENOMEM;
+ VFS_DEBUG(ctx,vp, "ATTRLIST - ERROR: cannot allocate relpath buffer");
+ goto out;
+ }
+ bzero(relpathptr, MAXPATHLEN);
+ }
+
/*
* Compute variable-space requirements.
*/
error = calc_varsize(vp, alp, vap, &varsize, fullpathptr, &fullpathlen,
- &vname, &cnp, &cnl);
+ relpathptr, &relpathlen, &vname, &cnp, &cnl);
if (error)
goto out;
/* common attributes ************************************************/
error = attr_pack_common(ctx, vp, alp, &ab, vap, proc_is64, cnp, cnl,
- fullpathptr, fullpathlen, return_valid, pack_invalid, vtype, is_bulk);
+ fullpathptr, fullpathlen, return_valid, pack_invalid, vtype, is_bulk);
/* directory attributes *********************************************/
if (!error && alp->dirattr && (vtype == VDIR)) {
- error = attr_pack_dir(vp, alp, &ab, vap);
+ error = attr_pack_dir(vp, alp, &ab, vap, return_valid, pack_invalid);
}
/* file attributes **************************************************/
pack_invalid, is_bulk);
}
+ /* common extended attributes *****************************************/
+ if (!error && use_fork) {
+ error = attr_pack_common_extended(vp, alp, &ab, relpathptr, relpathlen,
+ vap, return_valid, pack_invalid);
+ }
+
if (error)
goto out;
vnode_putname(vname);
if (fullpathptr)
kfree(fullpathptr, MAXPATHLEN);
+ if (relpathptr)
+ kfree(relpathptr, MAXPATHLEN);
if (ab.base != NULL && alloc_local_buf)
FREE(ab.base, M_TEMP);
return (error);
vap->va_active = 0;
error = getattrlist_setupvattr_all(alp, vap, v_type, &fixedsize,
- proc_is64bit(vfs_context_proc(ctx)));
+ proc_is64bit(vfs_context_proc(ctx)), options & FSOPT_ATTR_CMN_EXTENDED);
if (error) {
VFS_DEBUG(ctx, vp,
static int
getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
user_addr_t attributeBuffer, size_t bufferSize, uint64_t options,
- enum uio_seg segflg, char* alt_name, struct ucred *file_cred)
+ enum uio_seg segflg, char* authoritative_name, struct ucred *file_cred)
{
struct vnode_attr va;
kauth_action_t action;
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));
#endif /* MAC */
/*
- * It is legal to request volume or file attributes,
- * but not both.
+ * It is legal to request volume or file attributes, but not both.
+ *
+ * 26903449 fork attributes can also be requested, but only if they're
+ * interpreted as new, common attributes
*/
if (alp->volattr) {
- if (alp->fileattr || alp->dirattr || alp->forkattr) {
+ if (alp->fileattr || alp->dirattr || (alp->forkattr && !use_fork)) {
error = EINVAL;
- VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: mixed volume/file/directory/fork attributes");
+ VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: mixed volume/file/directory attributes");
goto out;
}
/* handle volume attribute request */
goto out;
}
+ /* common extended attributes require FSOPT_ATTR_CMN_EXTENDED option */
+ if (!(use_fork) && (alp->forkattr & ATTR_CMNEXT_VALIDMASK)) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /* FSOPT_ATTR_CMN_EXTENDED requires forkattrs are not referenced */
+ if ((options & FSOPT_ATTR_CMN_EXTENDED) && (alp->forkattr & (ATTR_FORK_VALIDMASK))) {
+ error = EINVAL;
+ goto out;
+ }
+
/* Check for special packing semantics */
return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) ? 1 : 0;
pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS) ? 1 : 0;
if (pack_invalid) {
/* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */
- if (!return_valid || alp->forkattr) {
+ if (!return_valid || (alp->forkattr && !use_fork)) {
error = EINVAL;
goto out;
}
/*
* Set up the vnode_attr structure and authorise.
*/
- if ((error = getattrlist_setupvattr(alp, &va, &fixedsize, &action, proc_is64, (vtype == VDIR))) != 0) {
+ if ((error = getattrlist_setupvattr(alp, &va, &fixedsize, &action, proc_is64, (vtype == VDIR), use_fork)) != 0) {
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed");
goto out;
}
VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: cannot allocate va_name buffer");
goto out;
}
+ /*
+ * If we have an authoritative_name, prefer that name.
+ *
+ * N.B. Since authoritative_name implies this is coming from getattrlistbulk,
+ * we know the name is authoritative. For /dev/fd, we want to use the file
+ * descriptor as the name not the underlying name of the associate vnode in a
+ * particular file system.
+ */
+ if (authoritative_name) {
+ /* Don't ask the file system */
+ VATTR_CLEAR_ACTIVE(&va, va_name);
+ strlcpy(va_name, authoritative_name, MAXPATHLEN);
+ }
}
- va.va_name = va_name;
+ va.va_name = authoritative_name ? NULL : va_name;
/*
* Call the filesystem.
#endif
/*
- * If ATTR_CMN_NAME is not supported by filesystem and the
- * caller has provided a name, use that.
+ * It we ask for the name, i.e., vname is non null and
+ * we have an authoritative name, then reset va_name is
+ * active and if needed set va_name is supported.
+ *
* A (buggy) filesystem may change fields which belong
* to us. We try to deal with that here as well.
*/
va.va_active = va_active;
- if (alt_name && va_name &&
- !(VATTR_IS_SUPPORTED(&va, va_name))) {
- strlcpy(va_name, alt_name, MAXPATHLEN);
- VATTR_SET_SUPPORTED(&va, va_name);
+ if (authoritative_name && va_name) {
+ VATTR_SET_ACTIVE(&va, va_name);
+ if (!(VATTR_IS_SUPPORTED(&va, va_name))) {
+ VATTR_SET_SUPPORTED(&va, va_name);
+ }
}
va.va_name = va_name;
}
fsiz = 0;
(void)getattrlist_setupvattr(&al, NULL, (ssize_t *)&fsiz,
&action, proc_is64bit(vfs_context_proc(ctx)),
- (vnode_vtype(vp) == VDIR));
+ (vnode_vtype(vp) == VDIR), (options & FSOPT_ATTR_CMN_EXTENDED));
namelen = strlen(namebuf);
vsiz = namelen + 1;
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;
fvdata = NULL;
eofflag = 0;
ctx = vfs_context_current();
+ ut = get_bsdthread_info(current_thread());
segflg = IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : UIO_USERSPACE32;
if ((fp->f_fglob->fg_flag & FREAD) == 0) {
if (uap->options & FSOPT_LIST_SNAPSHOT) {
vnode_t snapdvp;
- if (!vfs_context_issuser(ctx)) {
- error = EPERM;
- goto out;
- }
-
if (!vnode_isvroot(dvp)) {
error = EINVAL;
goto out;
va.va_name = va_name;
(void)getattrlist_setupvattr_all(&al, &va, VNON, NULL,
- IS_64BIT_PROCESS(p));
+ IS_64BIT_PROCESS(p), (uap->options & FSOPT_ATTR_CMN_EXTENDED));
+ /*
+ * Set UT_KERN_RAGE_VNODES to cause all vnodes created by the
+ * filesystem to be rapidly aged.
+ */
+ ut->uu_flag |= UT_KERN_RAGE_VNODES;
error = VNOP_GETATTRLISTBULK(dvp, &al, &va, auio, NULL,
options, &eofflag, &count, ctx);
+ ut->uu_flag &= ~UT_KERN_RAGE_VNODES;
FREE(va_name, M_TEMP);
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) {
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);
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) {
return error;
}
+int
+setattrlistat(proc_t p, struct setattrlistat_args *uap, __unused int32_t *retval)
+{
+ struct setattrlist_args ap;
+ struct vfs_context *ctx;
+ struct nameidata nd;
+ vnode_t vp = NULLVP;
+ uint32_t nameiflags;
+ int error;
+
+ ctx = vfs_context_current();
+
+ AUDIT_ARG(fd, uap->fd);
+ /*
+ * Look up the file.
+ */
+ nameiflags = AUDITVNPATH1;
+ if (!(uap->options & FSOPT_NOFOLLOW))
+ nameiflags |= FOLLOW;
+ NDINIT(&nd, LOOKUP, OP_SETATTR, nameiflags, UIO_USERSPACE, uap->path, ctx);
+ if ((error = nameiat(&nd, uap->fd)) != 0)
+ goto out;
+ vp = nd.ni_vp;
+ nameidone(&nd);
+
+ ap.path = 0;
+ ap.alist = uap->alist;
+ ap.attributeBuffer = uap->attributeBuffer;
+ ap.bufferSize = uap->bufferSize;
+ ap.options = uap->options;
+
+ error = setattrlist_internal(vp, &ap, p, ctx);
+out:
+ if (vp)
+ vnode_put(vp);
+ return (error);
+}
+
int
fsetattrlist(proc_t p, struct fsetattrlist_args *uap, __unused int32_t *retval)
{