X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/bb59bff194111743b33cc36712410b5656329d3c..e8c3f78193f1895ea514044358b93b1add9322f3:/bsd/vfs/vfs_attrlist.c diff --git a/bsd/vfs/vfs_attrlist.c b/bsd/vfs/vfs_attrlist.c index 54d0323f8..cd8cbacad 100644 --- a/bsd/vfs/vfs_attrlist.c +++ b/bsd/vfs/vfs_attrlist.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995-2014 Apple Inc. All rights reserved. + * Copyright (c) 1995-2018 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -51,7 +51,7 @@ #include #include #include -#include +#include #if CONFIG_MACF #include @@ -270,7 +270,7 @@ attrlist_pack_string(struct _attrlist_buf *ab, const char *source, ssize_t count #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) @@ -340,7 +340,7 @@ static struct getvolattrlist_attrtab getvolattrlist_common_tab[] = { 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)}, @@ -357,6 +357,8 @@ static struct getvolattrlist_attrtab getvolattrlist_vol_tab[] = { {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} @@ -364,7 +366,7 @@ static struct getvolattrlist_attrtab getvolattrlist_vol_tab[] = { 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; @@ -384,7 +386,7 @@ getvolattrlist_parsetab(struct getvolattrlist_attrtab *tab, attrgroup_t attrs, s *sizep += tab->size; } } - } while ((++tab)->attr != 0); + } while (((++tab)->attr != 0) && (--maxiter > 0)); /* check to make sure that we recognised all of the passed-in attributes */ if (attrs & ~recognised) @@ -400,6 +402,8 @@ static int 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. @@ -411,13 +415,14 @@ getvolattrlist_setupvfsattr(struct attrlist *alp, struct vfs_attr *vsp, ssize_t 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); @@ -473,7 +478,7 @@ struct getattrlist_attrtab { static struct getattrlist_attrtab getattrlist_common_tab[] = { {ATTR_CMN_NAME, VATTR_BIT(va_name), sizeof(struct attrreference), KAUTH_VNODE_READ_ATTRIBUTES}, {ATTR_CMN_DEVID, 0, sizeof(dev_t), KAUTH_VNODE_READ_ATTRIBUTES}, - {ATTR_CMN_FSID, VATTR_BIT(va_fsid), sizeof(fsid_t), KAUTH_VNODE_READ_ATTRIBUTES}, + {ATTR_CMN_FSID, 0, sizeof(fsid_t), KAUTH_VNODE_READ_ATTRIBUTES}, {ATTR_CMN_OBJTYPE, 0, sizeof(fsobj_type_t), KAUTH_VNODE_READ_ATTRIBUTES}, {ATTR_CMN_OBJTAG, 0, sizeof(fsobj_tag_t), KAUTH_VNODE_READ_ATTRIBUTES}, {ATTR_CMN_OBJID, VATTR_BIT(va_fileid) | VATTR_BIT(va_linkid), sizeof(fsobj_id_t), KAUTH_VNODE_READ_ATTRIBUTES}, @@ -510,6 +515,9 @@ static struct getattrlist_attrtab getattrlist_dir_tab[] = { {ATTR_DIR_LINKCOUNT, VATTR_BIT(va_dirlinkcount), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES}, {ATTR_DIR_ENTRYCOUNT, VATTR_BIT(va_nchildren), sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES}, {ATTR_DIR_MOUNTSTATUS, 0, sizeof(uint32_t), KAUTH_VNODE_READ_ATTRIBUTES}, + {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[] = { @@ -523,7 +531,15 @@ 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) @@ -550,13 +566,19 @@ static struct getattrlist_attrtab getattrlistbulk_file_tab[] = { {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 | \ @@ -576,8 +598,7 @@ static struct getattrlist_attrtab getattrlistbulk_file_tab[] = { 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) @@ -590,11 +611,13 @@ static struct getattrlist_attrtab getattrlistbulk_file_tab[] = { 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) { @@ -619,7 +642,7 @@ getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t 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) @@ -632,7 +655,7 @@ getattrlist_parsetab(struct getattrlist_attrtab *tab, attrgroup_t attrs, * the data from a filesystem. */ static int -getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, int is_64bit, int isdir) +getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *sizep, kauth_action_t *actionp, int is_64bit, int isdir, int use_fork) { int error; @@ -642,13 +665,16 @@ getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *si *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); @@ -660,7 +686,7 @@ getattrlist_setupvattr(struct attrlist *alp, struct vnode_attr *vap, ssize_t *si */ static int getattrlist_setupvattr_all(struct attrlist *alp, struct vnode_attr *vap, - enum vtype obj_type, ssize_t *fixedsize, int is_64bit) + enum vtype obj_type, ssize_t *fixedsize, int is_64bit, int use_fork) { int error = 0; @@ -672,12 +698,14 @@ getattrlist_setupvattr_all(struct attrlist *alp, struct vnode_attr *vap, } if (alp->commonattr) { error = getattrlist_parsetab(getattrlist_common_tab, - alp->commonattr, vap, fixedsize, NULL, is_64bit); + alp->commonattr, vap, fixedsize, NULL, is_64bit, + sizeof(getattrlist_common_tab)/sizeof(getattrlist_common_tab[0])); if (!error) { /* Ignore any errrors from the bulk table */ (void)getattrlist_parsetab(getattrlistbulk_common_tab, - alp->commonattr, vap, fixedsize, NULL, is_64bit); + 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. @@ -688,17 +716,33 @@ getattrlist_setupvattr_all(struct attrlist *alp, struct vnode_attr *vap, 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])); } } @@ -709,8 +753,10 @@ int vfs_setup_vattr_from_attrlist(struct attrlist *alp, struct vnode_attr *vap, enum vtype obj_vtype, ssize_t *attrs_fixed_sizep, vfs_context_t ctx) { + // 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)); } @@ -722,7 +768,7 @@ vfs_setup_vattr_from_attrlist(struct attrlist *alp, struct vnode_attr *vap, * missing attributes from the file system */ static void -getattrlist_fixupattrs(attribute_set_t *asp, struct vnode_attr *vap) +getattrlist_fixupattrs(attribute_set_t *asp, struct vnode_attr *vap, int use_fork) { struct getattrlist_attrtab *tab; @@ -776,6 +822,16 @@ getattrlist_fixupattrs(attribute_set_t *asp, struct vnode_attr *vap) } } while ((++tab)->attr != 0); } + if (use_fork && asp->forkattr) { + tab = getattrlist_common_extended_tab; + do { + if ((tab->attr & asp->forkattr) && + (tab->bits & vap->va_active) && + (vap->va_supported & tab->bits) == 0) { + asp->forkattr &= ~tab->attr; + } + } while ((++tab)->attr != 0); + } } static int @@ -908,17 +964,18 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, } } -#if CONFIG_MACF - error = mac_mount_check_getattr(ctx, mnt, &vs); - if (error != 0) - goto out; -#endif VFS_DEBUG(ctx, vp, "ATTRLIST - calling to get %016llx with supported %016llx", vs.f_active, vs.f_supported); if ((error = vfs_getattr(mnt, &vs, ctx)) != 0) { VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error); goto out; } - +#if CONFIG_MACF + error = mac_mount_check_getattr(ctx, mnt, &vs); + if (error != 0) { + VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d", error); + goto out; + } +#endif /* * Did we ask for something the filesystem doesn't support? */ @@ -951,7 +1008,7 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, 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; @@ -1018,7 +1075,13 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not fetch attributes from root vnode", vp); goto out; } - +#if CONFIG_MACF + error = mac_vnode_check_getattr(ctx, NOCRED, vp, &va); + if (error != 0) { + VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d for root vnode", error); + goto out; + } +#endif if (VATTR_IS_ACTIVE(&va, va_encoding) && !VATTR_IS_SUPPORTED(&va, va_encoding)) { if (!return_valid || pack_invalid) @@ -1073,12 +1136,33 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, * Note that since we won't ever copy out more than the caller requested, * we never need to allocate more than they offer. */ - 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; @@ -1098,6 +1182,10 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, 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; @@ -1167,6 +1255,7 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, * This attribute isn't really Finder Info, at least for HFS. */ if (vp->v_tag == VT_HFS) { +#define HFS_GET_BOOT_INFO (FCNTL_FS_SPECIFIC_BASE + 0x00004) error = VNOP_IOCTL(vp, HFS_GET_BOOT_INFO, (caddr_t)&f, 0, ctx); if (error == 0) { attrlist_pack_fixed(&ab, f, sizeof(f)); @@ -1331,6 +1420,21 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, vs.f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] &= ~VOL_CAP_INT_EXTENDED_SECURITY; } vs.f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] |= VOL_CAP_INT_EXTENDED_SECURITY; + + /* + * if the filesystem doesn't mark either VOL_CAP_FMT_NO_IMMUTABLE_FILES + * or VOL_CAP_FMT_NO_PERMISSIONS as valid, assume they're not supported + */ + if (!(vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_NO_IMMUTABLE_FILES)) { + vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~VOL_CAP_FMT_NO_IMMUTABLE_FILES; + vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_IMMUTABLE_FILES; + } + + if (!(vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_NO_PERMISSIONS)) { + vs.f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] &= ~VOL_CAP_FMT_NO_PERMISSIONS; + vs.f_capabilities.valid[VOL_CAPABILITIES_FORMAT] |= VOL_CAP_FMT_NO_PERMISSIONS; + } + ATTR_PACK(&ab, vs.f_capabilities); ab.actual.volattr |= ATTR_VOL_CAPABILITIES; } @@ -1338,6 +1442,14 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, ATTR_PACK(&ab, vs.f_uuid); ab.actual.volattr |= ATTR_VOL_UUID; } + if (alp->volattr & ATTR_VOL_QUOTA_SIZE) { + ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_quota); + ab.actual.volattr |= ATTR_VOL_QUOTA_SIZE; + } + if (alp->volattr & ATTR_VOL_RESERVED_SIZE) { + ATTR_PACK_CAST(&ab, off_t, vs.f_bsize * vs.f_reserved); + ab.actual.volattr |= ATTR_VOL_RESERVED_SIZE; + } if (alp->volattr & ATTR_VOL_ATTRIBUTES) { /* fix up volume attribute information */ @@ -1369,10 +1481,11 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, * of the result buffer, even if we copied less out. The caller knows how big a buffer * they gave us, so they can always check for truncation themselves. */ - *(uint32_t *)ab.base = (options & FSOPT_REPORT_FULLSIZE) ? ab.needed : imin(ab.allocated, ab.needed); - + *(uint32_t *)ab.base = (options & FSOPT_REPORT_FULLSIZE) ? 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 */ @@ -1384,9 +1497,9 @@ getvolattrlist(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, 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) @@ -1446,14 +1559,6 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp, struct attrlist *alp, } else if (VATTR_IS_SUPPORTED(vap, va_fsid64)) { ATTR_PACK8((*abp), vap->va_fsid64); abp->actual.commonattr |= ATTR_CMN_FSID; - } else if (VATTR_IS_SUPPORTED(vap, va_fsid)) { - fsid_t fsid; - - /* va_fsid is 32 bits */ - fsid.val[0] = vap->va_fsid; - fsid.val[1] = 0; - ATTR_PACK8((*abp), fsid); - abp->actual.commonattr |= ATTR_CMN_FSID; } else if (!return_valid || pack_invalid) { fsid_t fsid = {{0}}; @@ -1483,45 +1588,64 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp, struct attrlist *alp, } } if (alp->commonattr & ATTR_CMN_OBJID) { - fsobj_id_t f; /* * Carbon can't deal with us reporting the target ID * for links. So we ask the filesystem to give us the * source ID as well, and if it gives us one, we use * it instead. */ - if (VATTR_IS_SUPPORTED(vap, va_linkid)) { - f.fid_objno = vap->va_linkid; + if (vap->va_vaflags & VA_64BITOBJIDS) { + if (VATTR_IS_SUPPORTED(vap, va_linkid)) { + ATTR_PACK8((*abp), vap->va_linkid); + } else { + ATTR_PACK8((*abp), vap->va_fileid); + } } else { - f.fid_objno = vap->va_fileid; + fsobj_id_t f; + if (VATTR_IS_SUPPORTED(vap, va_linkid)) { + f.fid_objno = (uint32_t)vap->va_linkid; + } else { + f.fid_objno = (uint32_t)vap->va_fileid; + } + f.fid_generation = 0; + ATTR_PACK8((*abp), f); } - f.fid_generation = 0; - ATTR_PACK8((*abp), f); abp->actual.commonattr |= ATTR_CMN_OBJID; } if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) { - fsobj_id_t f; /* * Carbon can't deal with us reporting the target ID * for links. So we ask the filesystem to give us the * source ID as well, and if it gives us one, we use * it instead. */ - if (VATTR_IS_SUPPORTED(vap, va_linkid)) { - f.fid_objno = vap->va_linkid; + if (vap->va_vaflags & VA_64BITOBJIDS) { + if (VATTR_IS_SUPPORTED(vap, va_linkid)) { + ATTR_PACK8((*abp), vap->va_linkid); + } else { + ATTR_PACK8((*abp), vap->va_fileid); + } } else { - f.fid_objno = vap->va_fileid; + fsobj_id_t f; + if (VATTR_IS_SUPPORTED(vap, va_linkid)) { + f.fid_objno = (uint32_t)vap->va_linkid; + } else { + f.fid_objno = (uint32_t)vap->va_fileid; + } + f.fid_generation = 0; + ATTR_PACK8((*abp), f); } - f.fid_generation = 0; - ATTR_PACK8((*abp), f); abp->actual.commonattr |= ATTR_CMN_OBJPERMANENTID; } if (alp->commonattr & ATTR_CMN_PAROBJID) { - fsobj_id_t f; - - f.fid_objno = vap->va_parentid; /* could be lossy here! */ - f.fid_generation = 0; - ATTR_PACK8((*abp), f); + if (vap->va_vaflags & VA_64BITOBJIDS) { + ATTR_PACK8((*abp), vap->va_parentid); + } else { + fsobj_id_t f; + f.fid_objno = (uint32_t)vap->va_parentid; + f.fid_generation = 0; + ATTR_PACK8((*abp), f); + } abp->actual.commonattr |= ATTR_CMN_PAROBJID; } if (alp->commonattr & ATTR_CMN_SCRIPT) { @@ -1749,8 +1873,10 @@ attr_pack_common(vfs_context_t ctx, struct vnode *vp, struct attrlist *alp, } if (alp->commonattr & ATTR_CMN_FULLPATH) { - attrlist_pack_string (abp, fullpathptr, fullpathlen); - abp->actual.commonattr |= ATTR_CMN_FULLPATH; + if (vp) { + attrlist_pack_string (abp, fullpathptr, fullpathlen); + abp->actual.commonattr |= ATTR_CMN_FULLPATH; + } } if (alp->commonattr & ATTR_CMN_ADDEDTIME) { @@ -1777,7 +1903,7 @@ out: 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); @@ -1819,6 +1945,43 @@ attr_pack_dir(struct vnode *vp, struct attrlist *alp, struct _attrlist_buf *abp, ATTR_PACK4((*abp), mntstat); abp->actual.dirattr |= ATTR_DIR_MOUNTSTATUS; } + if (alp->dirattr & ATTR_DIR_ALLOCSIZE) { + if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) { + ATTR_PACK8((*abp), vap->va_data_alloc); + abp->actual.dirattr |= ATTR_DIR_ALLOCSIZE; + } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) { + ATTR_PACK8((*abp), vap->va_total_alloc); + abp->actual.dirattr |= ATTR_DIR_ALLOCSIZE; + } else if (!return_valid || pack_invalid) { + uint64_t zero_val = 0; + ATTR_PACK8((*abp), zero_val); + } + } + if (alp->dirattr & ATTR_DIR_IOBLOCKSIZE) { + if (VATTR_IS_SUPPORTED(vap, va_iosize)) { + ATTR_PACK4((*abp), vap->va_iosize); + abp->actual.dirattr |= ATTR_DIR_IOBLOCKSIZE; + } else if (!return_valid || pack_invalid) { + ATTR_PACK4((*abp), 0); + } + } + /* + * If the filesystem does not support datalength + * or dataallocsize, then we infer that totalsize and + * totalalloc are substitutes. + */ + if (alp->dirattr & ATTR_DIR_DATALENGTH) { + if (VATTR_IS_SUPPORTED(vap, va_data_size)) { + ATTR_PACK8((*abp), vap->va_data_size); + abp->actual.dirattr |= ATTR_DIR_DATALENGTH; + } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) { + ATTR_PACK8((*abp), vap->va_total_size); + abp->actual.dirattr |= ATTR_DIR_DATALENGTH; + } else if (!return_valid || pack_invalid) { + uint64_t zero_val = 0; + ATTR_PACK8((*abp), zero_val); + } + } return 0; } @@ -1899,7 +2062,7 @@ attr_pack_file(vfs_context_t ctx, struct vnode *vp, struct attrlist *alp, if (VATTR_IS_SUPPORTED(vap, va_data_size)) { totalsize += vap->va_data_size; - } else { + } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) { totalsize += vap->va_total_size; } @@ -1924,7 +2087,7 @@ attr_pack_file(vfs_context_t ctx, struct vnode *vp, struct attrlist *alp, */ if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) { totalalloc += vap->va_data_alloc; - } else { + } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) { totalalloc += vap->va_total_alloc; } @@ -1940,8 +2103,12 @@ attr_pack_file(vfs_context_t ctx, struct vnode *vp, struct attrlist *alp, } } if (alp->fileattr & ATTR_FILE_IOBLOCKSIZE) { - ATTR_PACK4((*abp), vap->va_iosize); - abp->actual.fileattr |= ATTR_FILE_IOBLOCKSIZE; + if (VATTR_IS_SUPPORTED(vap, va_iosize)) { + ATTR_PACK4((*abp), vap->va_iosize); + abp->actual.fileattr |= ATTR_FILE_IOBLOCKSIZE; + } else if (!return_valid || pack_invalid) { + ATTR_PACK4((*abp), 0); + } } if (alp->fileattr & ATTR_FILE_CLUMPSIZE) { if (!return_valid || pack_invalid) { @@ -1980,18 +2147,26 @@ attr_pack_file(vfs_context_t ctx, struct vnode *vp, struct attrlist *alp, if (alp->fileattr & ATTR_FILE_DATALENGTH) { if (VATTR_IS_SUPPORTED(vap, va_data_size)) { ATTR_PACK8((*abp), vap->va_data_size); - } else { + abp->actual.fileattr |= ATTR_FILE_DATALENGTH; + } else if (VATTR_IS_SUPPORTED(vap, va_total_size)){ ATTR_PACK8((*abp), vap->va_total_size); + abp->actual.fileattr |= ATTR_FILE_DATALENGTH; + } else if (!return_valid || pack_invalid) { + uint64_t zero_val = 0; + ATTR_PACK8((*abp), zero_val); } - abp->actual.fileattr |= ATTR_FILE_DATALENGTH; } if (alp->fileattr & ATTR_FILE_DATAALLOCSIZE) { if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) { ATTR_PACK8((*abp), vap->va_data_alloc); - } else { + abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE; + } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)){ ATTR_PACK8((*abp), vap->va_total_alloc); + abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE; + } else if (!return_valid || pack_invalid) { + uint64_t zero_val = 0; + ATTR_PACK8((*abp), zero_val); } - abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE; } /* already got the resource fork size/allocation above */ if (alp->fileattr & ATTR_FILE_RSRCLENGTH) { @@ -2024,6 +2199,48 @@ out: return (error); } +/* + * Pack FORKATTR attributes into a user buffer. + * alp is a pointer to the bitmap of attributes required. + * abp is the state of the attribute filling operation. + * The attribute data (along with some other fields that are required + * are in ad. + */ +static errno_t +attr_pack_common_extended(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) @@ -2101,7 +2318,8 @@ vattr_get_alt_data(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap, 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; @@ -2168,6 +2386,25 @@ calc_varsize(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap, *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. * @@ -2208,11 +2445,14 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp, 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; @@ -2220,6 +2460,8 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp, cnl = 0; fullpathptr = NULL; fullpathlen = 0; + relpathptr = NULL; + relpathlen = 0; error = 0; alloc_local_buf = 0; @@ -2246,14 +2488,14 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp, 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; } @@ -2263,20 +2505,33 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp, goto out; } - if (alp->commonattr & (ATTR_CMN_FULLPATH)) { + //if a path is requested, allocate a temporary buffer to build it + if (vp && (alp->commonattr & (ATTR_CMN_FULLPATH))) { fullpathptr = (char*) kalloc(MAXPATHLEN); if (fullpathptr == NULL) { error = ENOMEM; VFS_DEBUG(ctx,vp, "ATTRLIST - ERROR: cannot allocate fullpath buffer"); goto out; } + 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; @@ -2394,11 +2649,11 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp, /* 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 **************************************************/ @@ -2407,6 +2662,12 @@ vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp, 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; @@ -2466,6 +2727,8 @@ 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); @@ -2491,18 +2754,7 @@ vfs_attr_pack(vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options, vap->va_active = 0; error = getattrlist_setupvattr_all(alp, vap, v_type, &fixedsize, - proc_is64bit(vfs_context_proc(ctx))); - - /* - * Ugly hack to correctly report fsids. vs_fsid is 32 bits and - * there is va_fsid64 as well but filesystems have to say that - * both are supported so that the value can be used correctly. - * So we set va_fsid if the filesystem has only set va_fsid64. - */ - - if ((alp->commonattr & ATTR_CMN_FSID) && - VATTR_IS_SUPPORTED(vap, va_fsid64)) - VATTR_SET_SUPPORTED(vap, va_fsid); + proc_is64bit(vfs_context_proc(ctx)), options & FSOPT_ATTR_CMN_EXTENDED); if (error) { VFS_DEBUG(ctx, vp, @@ -2533,7 +2785,7 @@ out: 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) + enum uio_seg segflg, char* authoritative_name, struct ucred *file_cred) { struct vnode_attr va; kauth_action_t action; @@ -2546,6 +2798,11 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, 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)); @@ -2578,13 +2835,15 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, #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 */ @@ -2604,12 +2863,24 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, goto out; } + /* common extended attributes require FSOPT_ATTR_CMN_EXTENDED option */ + if (!(use_fork) && (alp->forkattr & ATTR_CMNEXT_VALIDMASK)) { + error = EINVAL; + goto out; + } + + /* FSOPT_ATTR_CMN_EXTENDED requires forkattrs are not referenced */ + if ((options & FSOPT_ATTR_CMN_EXTENDED) && (alp->forkattr & (ATTR_FORK_VALIDMASK))) { + error = EINVAL; + goto out; + } + /* Check for special packing semantics */ return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) ? 1 : 0; pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS) ? 1 : 0; if (pack_invalid) { /* FSOPT_PACK_INVAL_ATTRS requires ATTR_CMN_RETURNED_ATTRS */ - if (!return_valid || alp->forkattr) { + if (!return_valid || (alp->forkattr && !use_fork)) { error = EINVAL; goto out; } @@ -2625,7 +2896,7 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, /* * Set up the vnode_attr structure and authorise. */ - if ((error = getattrlist_setupvattr(alp, &va, &fixedsize, &action, proc_is64, (vtype == VDIR))) != 0) { + if ((error = getattrlist_setupvattr(alp, &va, &fixedsize, &action, proc_is64, (vtype == VDIR), use_fork)) != 0) { VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: setup for request failed"); goto out; } @@ -2635,7 +2906,6 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, } - if (va.va_active != 0) { uint64_t va_active = va.va_active; @@ -2650,9 +2920,22 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, 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. @@ -2661,19 +2944,40 @@ getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist *alp, VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: filesystem returned %d", error); goto out; } - +#if CONFIG_MACF + /* + * Give MAC polices a chance to reject or filter the + * attributes returned by the filesystem. Note that MAC + * policies are consulted *after* calling the filesystem + * because filesystems can return more attributes than + * were requested so policies wouldn't be authoritative + * is consulted beforehand. This also gives policies an + * opportunity to change the values of attributes + * retrieved. + */ + error = mac_vnode_check_getattr(ctx, file_cred, vp, &va); + if (error) { + VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: MAC framework returned %d", error); + goto out; + } +#else + (void)file_cred; +#endif /* - * 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; } @@ -2698,17 +3002,19 @@ fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval) vnode_t vp; int error; struct attrlist al; + struct fileproc *fp; ctx = vfs_context_current(); + vp = NULL; + fp = NULL; error = 0; if ((error = file_vnode(uap->fd, &vp)) != 0) return (error); - if ((error = vnode_getwithref(vp)) != 0) { - file_drop(uap->fd); - return(error); - } + if ((error = fp_lookup(p, uap->fd, &fp, 0)) != 0 || + (error = vnode_getwithref(vp)) != 0) + goto out; /* * Fetch the attribute request. @@ -2721,12 +3027,15 @@ fgetattrlist(proc_t p, struct fgetattrlist_args *uap, __unused int32_t *retval) error = getattrlist_internal(ctx, vp, &al, uap->attributeBuffer, uap->bufferSize, uap->options, (IS_64BIT_PROCESS(p) ? UIO_USERSPACE64 : \ - UIO_USERSPACE32), NULL); + UIO_USERSPACE32), NULL, + fp->f_fglob->fg_cred); out: - file_drop(uap->fd); + if (fp) + fp_drop(p, uap->fd, fp, 0); if (vp) vnode_put(vp); + file_drop(uap->fd); return error; } @@ -2760,7 +3069,7 @@ getattrlistat_internal(vfs_context_t ctx, user_addr_t path, vp = nd.ni_vp; error = getattrlist_internal(ctx, vp, alp, attributeBuffer, - bufferSize, options, segflg, NULL); + bufferSize, options, segflg, NULL, NOCRED); /* Retain the namei reference until the getattrlist completes. */ nameidone(&nd); @@ -3082,7 +3391,7 @@ get_error_attributes(vnode_t vp, struct attrlist *alp, uint64_t options, fsiz = 0; (void)getattrlist_setupvattr(&al, NULL, (ssize_t *)&fsiz, &action, proc_is64bit(vfs_context_proc(ctx)), - (vnode_vtype(vp) == VDIR)); + (vnode_vtype(vp) == VDIR), (options & FSOPT_ATTR_CMN_EXTENDED)); namelen = strlen(namebuf); vsiz = namelen + 1; @@ -3238,9 +3547,18 @@ readdirattr(vnode_t dvp, struct fd_vn_data *fvd, uio_t auio, } /* - * We have an iocount on the directory already + * We have an iocount on the directory already. + * + * Note that we supply NOCROSSMOUNT to the namei call as we attempt to acquire + * a vnode for this particular entry. This is because the native call will + * (likely) attempt to emit attributes based on its own metadata in order to avoid + * creating vnodes where posssible. If the native call is not going to walk + * up the vnode mounted-on chain in order to find the top-most mount point, then we + * should not either in this emulated readdir+getattrlist() approach. We + * will be responsible for setting DIR_MNTSTATUS_MNTPOINT on that directory that + * contains a mount point. */ - NDINIT(&nd, LOOKUP, OP_GETATTR, AUDITVNPATH1 | USEDVP, + NDINIT(&nd, LOOKUP, OP_GETATTR, (AUDITVNPATH1 | USEDVP | NOCROSSMOUNT), UIO_SYSSPACE, CAST_USER_ADDR_T(name_buffer), ctx); nd.ni_dvp = dvp; @@ -3264,7 +3582,8 @@ readdirattr(vnode_t dvp, struct fd_vn_data *fvd, uio_t auio, error = getattrlist_internal(ctx, vp, &al, CAST_USER_ADDR_T(kern_attr_buf), kern_attr_buf_siz, options | FSOPT_REPORT_FULLSIZE, UIO_SYSSPACE, - CAST_DOWN_EXPLICIT(char *, name_buffer)); + CAST_DOWN_EXPLICIT(char *, name_buffer), + NOCRED); nameidone(&nd); @@ -3385,6 +3704,7 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval) 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; @@ -3404,6 +3724,7 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval) 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) { @@ -3419,6 +3740,22 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval) goto out; } + if (uap->options & FSOPT_LIST_SNAPSHOT) { + vnode_t snapdvp; + + if (!vnode_isvroot(dvp)) { + error = EINVAL; + goto out; + } + + /* switch directory to snapshot directory */ + error = vnode_get_snapdir(dvp, &snapdvp, ctx); + if (error) + goto out; + vnode_put(dvp); + dvp = snapdvp; + } + if (dvp->v_type != VDIR) { error = ENOTDIR; goto out; @@ -3532,10 +3869,16 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval) 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); @@ -3556,8 +3899,10 @@ getattrlistbulk(proc_t p, struct getattrlistbulk_args *uap, int32_t *retval) eofflag = 0; count = 0; + ut->uu_flag |= UT_KERN_RAGE_VNODES; error = readdirattr(dvp, fvdata, auio, &al, options, &count, &eofflag, ctx); + ut->uu_flag &= ~UT_KERN_RAGE_VNODES; } if (count) { @@ -3646,6 +3991,19 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con 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); @@ -3738,7 +4096,8 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con } if (al.commonattr & ATTR_CMN_CHGTIME) { ATTR_UNPACK_TIME(va.va_change_time, proc_is64); - VATTR_SET_ACTIVE(&va, va_change_time); + al.commonattr &= ~ATTR_CMN_CHGTIME; + /*quietly ignore change time; advisory in man page*/ } if (al.commonattr & ATTR_CMN_ACCTIME) { ATTR_UNPACK_TIME(va.va_access_time, proc_is64); @@ -3772,6 +4131,10 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con if (al.commonattr & ATTR_CMN_FLAGS) { ATTR_UNPACK(va.va_flags); VATTR_SET_ACTIVE(&va, va_flags); +#if CONFIG_MACF + if ((error = mac_vnode_check_setflags(ctx, vp, va.va_flags)) != 0) + goto out; +#endif } if (al.commonattr & ATTR_CMN_EXTENDED_SECURITY) { @@ -3827,6 +4190,15 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con ATTR_UNPACK(va.va_guuid); VATTR_SET_ACTIVE(&va, va_guuid); } + if (al.commonattr & ATTR_CMN_ADDEDTIME) { + ATTR_UNPACK_TIME(va.va_addedtime, proc_is64); + VATTR_SET_ACTIVE(&va, va_addedtime); + } + /* Support setattrlist of data protection class */ + if (al.commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) { + ATTR_UNPACK(va.va_dataprotect_class); + VATTR_SET_ACTIVE(&va, va_dataprotect_class); + } /* volume */ if (al.volattr & ATTR_VOL_INFO) { @@ -3834,18 +4206,20 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con volname = cursor; ATTR_UNPACK(ar); /* attr_length cannot be 0! */ - if ((ar.attr_dataoffset < 0) || (ar.attr_length == 0)) { + if ((ar.attr_dataoffset < 0) || (ar.attr_length == 0) || + (ar.attr_length > uap->bufferSize) || + (uap->bufferSize - ar.attr_length < (unsigned)ar.attr_dataoffset)) { VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: bad offset supplied (2) ", ar.attr_dataoffset); error = EINVAL; goto out; } - volname += ar.attr_dataoffset; - if ((volname + ar.attr_length) > bufend) { + if (volname >= bufend - ar.attr_dataoffset - ar.attr_length) { error = EINVAL; VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: volume name too big for caller buffer"); goto out; } + volname += ar.attr_dataoffset; /* guarantee NUL termination */ volname[ar.attr_length - 1] = 0; } @@ -3913,12 +4287,19 @@ setattrlist_internal(vnode_t vp, struct setattrlist_args *uap, proc_t p, vfs_con goto out; } +#if CONFIG_MACF + mac_vnode_notify_setattrlist(ctx, vp, &al); + if (VATTR_IS_ACTIVE(&va, va_flags)) + mac_vnode_notify_setflags(ctx, vp, va.va_flags); +#endif + /* * Write the Finder Info if we have any. */ if (fndrinfo != NULL) { if (al.volattr & ATTR_VOL_INFO) { if (vp->v_tag == VT_HFS) { +#define HFS_SET_BOOT_INFO (FCNTL_FS_SPECIFIC_BASE + 0x00005) error = VNOP_IOCTL(vp, HFS_SET_BOOT_INFO, (caddr_t)fndrinfo, 0, ctx); if (error != 0) goto out; @@ -3999,6 +4380,44 @@ out: 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) {