+ uint32_t perms = 0;
+ int error = 0;
+
+ if ((alp->commonattr & ATTR_CMN_ERROR) &&
+ (!return_valid || pack_invalid)) {
+ ATTR_PACK4((*abp), 0);
+ abp->actual.commonattr |= ATTR_CMN_ERROR;
+ }
+ if (alp->commonattr & ATTR_CMN_NAME) {
+ attrlist_pack_string(abp, cnp, cnl);
+ abp->actual.commonattr |= ATTR_CMN_NAME;
+ }
+ if (alp->commonattr & ATTR_CMN_DEVID) {
+ if (vp) {
+ ATTR_PACK4((*abp),
+ vp->v_mount->mnt_vfsstat.f_fsid.val[0]);
+ abp->actual.commonattr |= ATTR_CMN_DEVID;
+ } else if (VATTR_IS_SUPPORTED(vap, va_devid)) {
+ ATTR_PACK4((*abp), vap->va_devid);
+ abp->actual.commonattr |= ATTR_CMN_DEVID;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_FSID) {
+ if (vp) {
+ ATTR_PACK8((*abp),
+ vp->v_mount->mnt_vfsstat.f_fsid);
+ abp->actual.commonattr |= ATTR_CMN_FSID;
+ } else if (VATTR_IS_SUPPORTED(vap, va_fsid64)) {
+ ATTR_PACK8((*abp), vap->va_fsid64);
+ abp->actual.commonattr |= ATTR_CMN_FSID;
+ } else if (!return_valid || pack_invalid) {
+ fsid_t fsid = {{0}};
+
+ ATTR_PACK8((*abp), fsid);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_OBJTYPE) {
+ if (vp) {
+ ATTR_PACK4((*abp), vtype);
+ abp->actual.commonattr |= ATTR_CMN_OBJTYPE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_objtype)) {
+ ATTR_PACK4((*abp), vap->va_objtype);
+ abp->actual.commonattr |= ATTR_CMN_OBJTYPE;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_OBJTAG) {
+ if (vp) {
+ ATTR_PACK4((*abp), vp->v_tag);
+ abp->actual.commonattr |= ATTR_CMN_OBJTAG;
+ } else if (VATTR_IS_SUPPORTED(vap, va_objtag)) {
+ ATTR_PACK4((*abp), vap->va_objtag);
+ abp->actual.commonattr |= ATTR_CMN_OBJTAG;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_OBJID) {
+ /*
+ * Carbon can't deal with us reporting the target ID
+ * for links. So we ask the filesystem to give us the
+ * source ID as well, and if it gives us one, we use
+ * it instead.
+ */
+ if (vap->va_vaflags & VA_64BITOBJIDS) {
+ if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
+ ATTR_PACK8((*abp), vap->va_linkid);
+ } else {
+ ATTR_PACK8((*abp), vap->va_fileid);
+ }
+ } else {
+ fsobj_id_t f;
+ if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
+ f.fid_objno = (uint32_t)vap->va_linkid;
+ } else {
+ f.fid_objno = (uint32_t)vap->va_fileid;
+ }
+ f.fid_generation = 0;
+ ATTR_PACK8((*abp), f);
+ }
+ abp->actual.commonattr |= ATTR_CMN_OBJID;
+ }
+ if (alp->commonattr & ATTR_CMN_OBJPERMANENTID) {
+ /*
+ * Carbon can't deal with us reporting the target ID
+ * for links. So we ask the filesystem to give us the
+ * source ID as well, and if it gives us one, we use
+ * it instead.
+ */
+ if (vap->va_vaflags & VA_64BITOBJIDS) {
+ if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
+ ATTR_PACK8((*abp), vap->va_linkid);
+ } else {
+ ATTR_PACK8((*abp), vap->va_fileid);
+ }
+ } else {
+ fsobj_id_t f;
+ if (VATTR_IS_SUPPORTED(vap, va_linkid)) {
+ f.fid_objno = (uint32_t)vap->va_linkid;
+ } else {
+ f.fid_objno = (uint32_t)vap->va_fileid;
+ }
+ f.fid_generation = 0;
+ ATTR_PACK8((*abp), f);
+ }
+ abp->actual.commonattr |= ATTR_CMN_OBJPERMANENTID;
+ }
+ if (alp->commonattr & ATTR_CMN_PAROBJID) {
+ if (vap->va_vaflags & VA_64BITOBJIDS) {
+ ATTR_PACK8((*abp), vap->va_parentid);
+ } else {
+ fsobj_id_t f;
+ f.fid_objno = (uint32_t)vap->va_parentid;
+ f.fid_generation = 0;
+ ATTR_PACK8((*abp), f);
+ }
+ abp->actual.commonattr |= ATTR_CMN_PAROBJID;
+ }
+ if (alp->commonattr & ATTR_CMN_SCRIPT) {
+ if (VATTR_IS_SUPPORTED(vap, va_encoding)) {
+ ATTR_PACK4((*abp), vap->va_encoding);
+ abp->actual.commonattr |= ATTR_CMN_SCRIPT;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0x7e);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_CRTIME) {
+ ATTR_PACK_TIME((*abp), vap->va_create_time, proc_is64);
+ abp->actual.commonattr |= ATTR_CMN_CRTIME;
+ }
+ if (alp->commonattr & ATTR_CMN_MODTIME) {
+ ATTR_PACK_TIME((*abp), vap->va_modify_time, proc_is64);
+ abp->actual.commonattr |= ATTR_CMN_MODTIME;
+ }
+ if (alp->commonattr & ATTR_CMN_CHGTIME) {
+ ATTR_PACK_TIME((*abp), vap->va_change_time, proc_is64);
+ abp->actual.commonattr |= ATTR_CMN_CHGTIME;
+ }
+ if (alp->commonattr & ATTR_CMN_ACCTIME) {
+ ATTR_PACK_TIME((*abp), vap->va_access_time, proc_is64);
+ abp->actual.commonattr |= ATTR_CMN_ACCTIME;
+ }
+ if (alp->commonattr & ATTR_CMN_BKUPTIME) {
+ ATTR_PACK_TIME((*abp), vap->va_backup_time, proc_is64);
+ abp->actual.commonattr |= ATTR_CMN_BKUPTIME;
+ }
+ /*
+ * They are requesting user access, we should obtain this before getting
+ * the finder info. For some network file systems this is a performance
+ * improvement.
+ */
+ if (alp->commonattr & ATTR_CMN_USERACCESS) { /* this is expensive */
+ if (vp && !is_bulk) {
+ if (vtype == VDIR) {
+ if (vnode_authorize(vp, NULL,
+ KAUTH_VNODE_ACCESS | KAUTH_VNODE_ADD_FILE |
+ KAUTH_VNODE_ADD_SUBDIRECTORY |
+ KAUTH_VNODE_DELETE_CHILD, ctx) == 0)
+ perms |= W_OK;
+
+ if (vnode_authorize(vp, NULL,
+ KAUTH_VNODE_ACCESS |
+ KAUTH_VNODE_LIST_DIRECTORY, ctx) == 0)
+ perms |= R_OK;
+
+ if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS |
+ KAUTH_VNODE_SEARCH, ctx) == 0)
+ perms |= X_OK;
+ } else {
+ if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS |
+ KAUTH_VNODE_WRITE_DATA, ctx) == 0)
+ perms |= W_OK;
+
+ if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_READ_DATA, ctx) == 0)
+ perms |= R_OK;
+ if (vnode_authorize(vp, NULL, KAUTH_VNODE_ACCESS | KAUTH_VNODE_EXECUTE, ctx) == 0)
+ perms |= X_OK;
+ }
+ } else if (is_bulk &&
+ VATTR_IS_SUPPORTED(vap, va_user_access)) {
+ perms = vap->va_user_access;
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_FNDRINFO) {
+ size_t fisize = 32;
+
+ error = 0;
+ if (vp && !is_bulk) {
+ uio_t auio;
+ char uio_buf[UIO_SIZEOF(1)];
+
+ if ((auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE,
+ UIO_READ, uio_buf, sizeof(uio_buf))) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ uio_addiov(auio, CAST_USER_ADDR_T(abp->fixedcursor),
+ fisize);
+ /* fisize may be reset to 0 after this call */
+ error = vn_getxattr(vp, XATTR_FINDERINFO_NAME, auio,
+ &fisize, XATTR_NOSECURITY, ctx);
+ uio_free(auio);
+
+ /*
+ * Default to zeros if its not available,
+ * unless ATTR_CMN_RETURNED_ATTRS was requested.
+ */
+ if (error &&
+ (!return_valid || pack_invalid) &&
+ ((error == ENOATTR) || (error == ENOENT) ||
+ (error == ENOTSUP) || (error == EPERM))) {
+ VFS_DEBUG(ctx, vp, "ATTRLIST - No system.finderinfo attribute, returning zeroes");
+ bzero(abp->fixedcursor, 32);
+ error = 0;
+ }
+
+ if (error == 0) {
+ abp->fixedcursor += 32;
+ abp->actual.commonattr |= ATTR_CMN_FNDRINFO;
+ } else if (!return_valid) {
+ goto out;
+ } else {
+ /*
+ * If we can inform the caller that we can't
+ * return this attribute, reset error and
+ * continue with the rest of the attributes.
+ */
+ error = 0;
+ }
+ } else if (VATTR_IS_SUPPORTED(vap, va_finderinfo)) {
+ bcopy(&vap->va_finderinfo[0], abp->fixedcursor, fisize);
+ abp->fixedcursor += fisize;
+ abp->actual.commonattr |= ATTR_CMN_FNDRINFO;
+ } else if (!return_valid || pack_invalid) {
+ bzero(abp->fixedcursor, fisize);
+ abp->fixedcursor += fisize;
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_OWNERID) {
+ ATTR_PACK4((*abp), vap->va_uid);
+ abp->actual.commonattr |= ATTR_CMN_OWNERID;
+ }
+ if (alp->commonattr & ATTR_CMN_GRPID) {
+ ATTR_PACK4((*abp), vap->va_gid);
+ abp->actual.commonattr |= ATTR_CMN_GRPID;
+ }
+ if (alp->commonattr & ATTR_CMN_ACCESSMASK) {
+ ATTR_PACK4((*abp), vap->va_mode);
+ abp->actual.commonattr |= ATTR_CMN_ACCESSMASK;
+ }
+ if (alp->commonattr & ATTR_CMN_FLAGS) {
+ ATTR_PACK4((*abp), vap->va_flags);
+ abp->actual.commonattr |= ATTR_CMN_FLAGS;
+ }
+ if (alp->commonattr & ATTR_CMN_GEN_COUNT) {
+ if (VATTR_IS_SUPPORTED(vap, va_write_gencount)) {
+ ATTR_PACK4((*abp), vap->va_write_gencount);
+ abp->actual.commonattr |= ATTR_CMN_GEN_COUNT;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+
+ if (alp->commonattr & ATTR_CMN_DOCUMENT_ID) {
+ if (VATTR_IS_SUPPORTED(vap, va_document_id)) {
+ ATTR_PACK4((*abp), vap->va_document_id);
+ abp->actual.commonattr |= ATTR_CMN_DOCUMENT_ID;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ /* We already obtain the user access, so just fill in the buffer here */
+ if (alp->commonattr & ATTR_CMN_USERACCESS) {
+#if CONFIG_MACF
+ if (!is_bulk && vp) {
+ /*
+ * Rather than MAC preceding DAC, in this case we want
+ * the smallest set of permissions granted by both MAC &
+ * DAC checks. We won't add back any permissions.
+ */
+ if (perms & W_OK)
+ if (mac_vnode_check_access(ctx, vp, W_OK) != 0)
+ perms &= ~W_OK;
+ if (perms & R_OK)
+ if (mac_vnode_check_access(ctx, vp, R_OK) != 0)
+ perms &= ~R_OK;
+ if (perms & X_OK)
+ if (mac_vnode_check_access(ctx, vp, X_OK) != 0)
+ perms &= ~X_OK;
+ }
+#endif /* MAC */
+ VFS_DEBUG(ctx, vp, "ATTRLIST - granting perms %d", perms);
+ if (!is_bulk && vp) {
+ ATTR_PACK4((*abp), perms);
+ abp->actual.commonattr |= ATTR_CMN_USERACCESS;
+ } else if (is_bulk && VATTR_IS_SUPPORTED(vap, va_user_access)) {
+ ATTR_PACK4((*abp), perms);
+ abp->actual.commonattr |= ATTR_CMN_USERACCESS;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_EXTENDED_SECURITY) {
+ if (VATTR_IS_SUPPORTED(vap, va_acl) && (vap->va_acl != NULL)) {
+ struct kauth_filesec fsec;
+ /*
+ * We want to return a kauth_filesec (for now), but all we have is a kauth_acl.
+ */
+ fsec.fsec_magic = KAUTH_FILESEC_MAGIC;
+ fsec.fsec_owner = kauth_null_guid;
+ fsec.fsec_group = kauth_null_guid;
+ attrlist_pack_variable2(abp, &fsec, __offsetof(struct kauth_filesec, fsec_acl), vap->va_acl, KAUTH_ACL_COPYSIZE(vap->va_acl));
+ abp->actual.commonattr |= ATTR_CMN_EXTENDED_SECURITY;
+ } else if (!return_valid || pack_invalid) {
+ attrlist_pack_variable(abp, NULL, 0);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_UUID) {
+ if (VATTR_IS_SUPPORTED(vap, va_uuuid)) {
+ ATTR_PACK(abp, vap->va_uuuid);
+ abp->actual.commonattr |= ATTR_CMN_UUID;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK(abp, kauth_null_guid);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_GRPUUID) {
+ if (VATTR_IS_SUPPORTED(vap, va_guuid)) {
+ ATTR_PACK(abp, vap->va_guuid);
+ abp->actual.commonattr |= ATTR_CMN_GRPUUID;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK(abp, kauth_null_guid);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_FILEID) {
+ ATTR_PACK8((*abp), vap->va_fileid);
+ abp->actual.commonattr |= ATTR_CMN_FILEID;
+ }
+ if (alp->commonattr & ATTR_CMN_PARENTID) {
+ ATTR_PACK8((*abp), vap->va_parentid);
+ abp->actual.commonattr |= ATTR_CMN_PARENTID;
+ }
+
+ if (alp->commonattr & ATTR_CMN_FULLPATH) {
+ if (vp) {
+ attrlist_pack_string (abp, fullpathptr, fullpathlen);
+ abp->actual.commonattr |= ATTR_CMN_FULLPATH;
+ }
+ }
+
+ if (alp->commonattr & ATTR_CMN_ADDEDTIME) {
+ if (VATTR_IS_SUPPORTED(vap, va_addedtime)) {
+ ATTR_PACK_TIME((*abp), vap->va_addedtime, proc_is64);
+ abp->actual.commonattr |= ATTR_CMN_ADDEDTIME;
+ } else if (!return_valid || pack_invalid) {
+ struct timespec zerotime = {0, 0};
+
+ ATTR_PACK_TIME((*abp), zerotime, proc_is64);
+ }
+ }
+ if (alp->commonattr & ATTR_CMN_DATA_PROTECT_FLAGS) {
+ if (VATTR_IS_SUPPORTED(vap, va_dataprotect_class)) {
+ ATTR_PACK4((*abp), vap->va_dataprotect_class);
+ abp->actual.commonattr |= ATTR_CMN_DATA_PROTECT_FLAGS;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+out:
+ return (error);
+}
+
+static errno_t
+attr_pack_dir(struct vnode *vp, struct attrlist *alp, struct _attrlist_buf *abp,
+ struct vnode_attr *vap, int return_valid, int pack_invalid)
+{
+ if (alp->dirattr & ATTR_DIR_LINKCOUNT) { /* full count of entries */
+ ATTR_PACK4((*abp), (uint32_t)vap->va_dirlinkcount);
+ abp->actual.dirattr |= ATTR_DIR_LINKCOUNT;
+ }
+ if (alp->dirattr & ATTR_DIR_ENTRYCOUNT) {
+ ATTR_PACK4((*abp), (uint32_t)vap->va_nchildren);
+ abp->actual.dirattr |= ATTR_DIR_ENTRYCOUNT;
+ }
+ if (alp->dirattr & ATTR_DIR_MOUNTSTATUS) {
+ uint32_t mntstat;
+
+ if (vp) {
+ /*
+ * The vnode that is passed down may either be a
+ * top level vnode of a mount stack or a mounted
+ * on vnode. In either case, the directory should
+ * be reported as a mount point.
+ */
+ if ((vp->v_flag & VROOT) || vnode_mountedhere(vp)) {
+ mntstat = DIR_MNTSTATUS_MNTPOINT;
+ } else {
+ mntstat = 0;
+ }
+#if CONFIG_TRIGGERS
+ /*
+ * Report back on active vnode triggers
+ * that can directly trigger a mount
+ */
+ if (vp->v_resolve &&
+ !(vp->v_resolve->vr_flags & VNT_NO_DIRECT_MOUNT)) {
+ mntstat |= DIR_MNTSTATUS_TRIGGER;
+ }
+#endif
+ } else {
+ mntstat = 0;
+ }
+
+ ATTR_PACK4((*abp), mntstat);
+ abp->actual.dirattr |= ATTR_DIR_MOUNTSTATUS;
+ }
+ if (alp->dirattr & ATTR_DIR_ALLOCSIZE) {
+ if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
+ ATTR_PACK8((*abp), vap->va_data_alloc);
+ abp->actual.dirattr |= ATTR_DIR_ALLOCSIZE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
+ ATTR_PACK8((*abp), vap->va_total_alloc);
+ abp->actual.dirattr |= ATTR_DIR_ALLOCSIZE;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+ if (alp->dirattr & ATTR_DIR_IOBLOCKSIZE) {
+ if (VATTR_IS_SUPPORTED(vap, va_iosize)) {
+ ATTR_PACK4((*abp), vap->va_iosize);
+ abp->actual.dirattr |= ATTR_DIR_IOBLOCKSIZE;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ /*
+ * If the filesystem does not support datalength
+ * or dataallocsize, then we infer that totalsize and
+ * totalalloc are substitutes.
+ */
+ if (alp->dirattr & ATTR_DIR_DATALENGTH) {
+ if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
+ ATTR_PACK8((*abp), vap->va_data_size);
+ abp->actual.dirattr |= ATTR_DIR_DATALENGTH;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
+ ATTR_PACK8((*abp), vap->va_total_size);
+ abp->actual.dirattr |= ATTR_DIR_DATALENGTH;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * The is_bulk parameter differentiates whether the function is called from
+ * getattrlist or getattrlistbulk. When coming in from getattrlistbulk,
+ * the corresponding va_* values are expected to be the values filled and no
+ * attempt is made to retrieve them by calling back into the filesystem.
+ */
+static errno_t
+attr_pack_file(vfs_context_t ctx, struct vnode *vp, struct attrlist *alp,
+ struct _attrlist_buf *abp, struct vnode_attr *vap, int return_valid,
+ int pack_invalid, int is_bulk)
+{
+ size_t rsize = 0;
+ uint64_t rlength = 0;
+ uint64_t ralloc = 0;
+ int error = 0;
+
+ /*
+ * Pre-fetch the rsrc attributes now so we only get them once.
+ * Fetch the resource fork size/allocation via xattr interface
+ */
+ if (vp && !is_bulk &&
+ (alp->fileattr & (ATTR_FILE_TOTALSIZE | ATTR_FILE_ALLOCSIZE |
+ ATTR_FILE_RSRCLENGTH | ATTR_FILE_RSRCALLOCSIZE))) {
+
+ error = vn_getxattr(vp, XATTR_RESOURCEFORK_NAME, NULL,
+ &rsize, XATTR_NOSECURITY, ctx);
+ if (error) {
+ if ((error == ENOENT) || (error == ENOATTR) ||
+ (error == ENOTSUP) || (error == EPERM) ||
+ (error == EACCES)) {
+ rsize = 0;
+ error = 0;
+ } else {
+ goto out;
+ }
+ }
+ rlength = rsize;
+
+ if (alp->fileattr & (ATTR_FILE_RSRCALLOCSIZE |
+ ATTR_FILE_ALLOCSIZE)) {
+ uint32_t blksize;
+
+ blksize = vp->v_mount->mnt_vfsstat.f_bsize;
+
+ if (blksize == 0) {
+ blksize = 512;
+ }
+ ralloc = roundup(rsize, blksize);
+ }
+ }
+
+ if (alp->fileattr & ATTR_FILE_LINKCOUNT) {
+ ATTR_PACK4((*abp), (uint32_t)vap->va_nlink);
+ abp->actual.fileattr |= ATTR_FILE_LINKCOUNT;
+ }
+ /*
+ * Note the following caveats for the TOTALSIZE and ALLOCSIZE attributes:
+ * We infer that if the filesystem does not support va_data_size or va_data_alloc
+ * it must not know about alternate forks. So when we need to gather
+ * the total size or total alloc, it's OK to substitute the total size for
+ * the data size below. This is because it is likely a flat filesystem and we must
+ * be using AD files to store the rsrc fork and EAs.
+ *
+ * Additionally, note that getattrlist is barred from being called on
+ * resource fork paths. (Search for CN_ALLOWRSRCFORK). So if the filesystem does
+ * support va_data_size, it is guaranteed to represent the data fork's size. This
+ * is an important distinction to make because when we call vnode_getattr on
+ * an HFS resource fork vnode, to get the size, it will vend out the resource
+ * fork's size (it only gets the size of the passed-in vnode).
+ */
+ if (alp->fileattr & ATTR_FILE_TOTALSIZE) {
+ if (!is_bulk) {
+ uint64_t totalsize = rlength;
+
+ if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
+ totalsize += vap->va_data_size;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
+ totalsize += vap->va_total_size;
+ }
+
+ ATTR_PACK8((*abp), totalsize);
+ abp->actual.fileattr |= ATTR_FILE_TOTALSIZE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_size)) {
+ ATTR_PACK8((*abp), vap->va_total_size);
+ abp->actual.fileattr |= ATTR_FILE_TOTALSIZE;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+ if (alp->fileattr & ATTR_FILE_ALLOCSIZE) {
+ if (!is_bulk) {
+ uint64_t totalalloc = ralloc;
+
+ /*
+ * If data_alloc is supported, then it must represent the
+ * data fork size.
+ */
+ if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
+ totalalloc += vap->va_data_alloc;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
+ totalalloc += vap->va_total_alloc;
+ }
+
+ ATTR_PACK8((*abp), totalalloc);
+ abp->actual.fileattr |= ATTR_FILE_ALLOCSIZE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)) {
+ ATTR_PACK8((*abp), vap->va_total_alloc);
+ abp->actual.fileattr |= ATTR_FILE_ALLOCSIZE;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+ if (alp->fileattr & ATTR_FILE_IOBLOCKSIZE) {
+ if (VATTR_IS_SUPPORTED(vap, va_iosize)) {
+ ATTR_PACK4((*abp), vap->va_iosize);
+ abp->actual.fileattr |= ATTR_FILE_IOBLOCKSIZE;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ if (alp->fileattr & ATTR_FILE_CLUMPSIZE) {
+ if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0); /* this value is deprecated */
+ abp->actual.fileattr |= ATTR_FILE_CLUMPSIZE;
+ }
+ }
+ if (alp->fileattr & ATTR_FILE_DEVTYPE) {
+ if (vp && (vp->v_type == VCHR || vp->v_type == VBLK)) {
+ uint32_t dev;
+
+ if (vp->v_specinfo != NULL) {
+ dev = vp->v_specinfo->si_rdev;
+ } else if (VATTR_IS_SUPPORTED(vap, va_rdev)) {
+ dev = vap->va_rdev;
+ } else {
+ dev = 0;
+ }
+ ATTR_PACK4((*abp), dev);
+ abp->actual.fileattr |= ATTR_FILE_DEVTYPE;
+ } else if (vp) {
+ ATTR_PACK4((*abp), 0);
+ abp->actual.fileattr |= ATTR_FILE_DEVTYPE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_rdev)) {
+ ATTR_PACK4((*abp), vap->va_rdev);
+ abp->actual.fileattr |= ATTR_FILE_DEVTYPE;
+ } else if (!return_valid || pack_invalid) {
+ ATTR_PACK4((*abp), 0);
+ }
+ }
+ /*
+ * If the filesystem does not support datalength
+ * or dataallocsize, then we infer that totalsize and
+ * totalalloc are substitutes.
+ */
+ if (alp->fileattr & ATTR_FILE_DATALENGTH) {
+ if (VATTR_IS_SUPPORTED(vap, va_data_size)) {
+ ATTR_PACK8((*abp), vap->va_data_size);
+ abp->actual.fileattr |= ATTR_FILE_DATALENGTH;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_size)){
+ ATTR_PACK8((*abp), vap->va_total_size);
+ abp->actual.fileattr |= ATTR_FILE_DATALENGTH;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+ if (alp->fileattr & ATTR_FILE_DATAALLOCSIZE) {
+ if (VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
+ ATTR_PACK8((*abp), vap->va_data_alloc);
+ abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_total_alloc)){
+ ATTR_PACK8((*abp), vap->va_total_alloc);
+ abp->actual.fileattr |= ATTR_FILE_DATAALLOCSIZE;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+ /* already got the resource fork size/allocation above */
+ if (alp->fileattr & ATTR_FILE_RSRCLENGTH) {
+ if (!is_bulk) {
+ ATTR_PACK8((*abp), rlength);
+ abp->actual.fileattr |= ATTR_FILE_RSRCLENGTH;
+ } else if (VATTR_IS_SUPPORTED(vap, va_rsrc_length)) {
+ ATTR_PACK8((*abp), vap->va_rsrc_length);
+ abp->actual.fileattr |= ATTR_FILE_RSRCLENGTH;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+ if (alp->fileattr & ATTR_FILE_RSRCALLOCSIZE) {
+ if (!is_bulk) {
+ ATTR_PACK8((*abp), ralloc);
+ abp->actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE;
+ } else if (VATTR_IS_SUPPORTED(vap, va_rsrc_alloc)) {
+ ATTR_PACK8((*abp), vap->va_rsrc_alloc);
+ abp->actual.fileattr |= ATTR_FILE_RSRCALLOCSIZE;
+ } else if (!return_valid || pack_invalid) {
+ uint64_t zero_val = 0;
+
+ ATTR_PACK8((*abp), zero_val);
+ }
+ }
+out:
+ return (error);
+}
+
+/*
+ * Pack FORKATTR attributes into a user buffer.
+ * alp is a pointer to the bitmap of attributes required.
+ * abp is the state of the attribute filling operation.
+ * The attribute data (along with some other fields that are required
+ * are in ad.
+ */
+static errno_t
+attr_pack_common_extended(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)
+{
+ /*
+ * There are a couple of special cases.
+ * If we are after object IDs, we can make do with va_fileid.
+ */
+ if ((alp->commonattr &
+ (ATTR_CMN_OBJID | ATTR_CMN_OBJPERMANENTID | ATTR_CMN_FILEID)) &&
+ !VATTR_IS_SUPPORTED(vap, va_linkid)) {
+ /* forget we wanted this */
+ VATTR_CLEAR_ACTIVE(vap, va_linkid);
+ }
+
+ /*
+ * Many filesystems don't know their parent object id.
+ * If necessary, attempt to derive it from the vnode.
+ */
+ if ((alp->commonattr & (ATTR_CMN_PAROBJID | ATTR_CMN_PARENTID)) &&
+ !VATTR_IS_SUPPORTED(vap, va_parentid) && vp && !is_bulk) {
+ 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(vap, va_fileid)) {
+ vap->va_parentid = lva.va_fileid;
+ VATTR_SET_SUPPORTED(vap, va_parentid);
+ }
+ vnode_put(dvp);
+ }
+ }
+ /*
+ * And we can report datasize/alloc from total.
+ */
+ if ((alp->fileattr & ATTR_FILE_DATALENGTH) &&
+ !VATTR_IS_SUPPORTED(vap, va_data_size)) {
+ VATTR_CLEAR_ACTIVE(vap, va_data_size);
+ }
+
+ if ((alp->fileattr & ATTR_FILE_DATAALLOCSIZE) &&
+ !VATTR_IS_SUPPORTED(vap, va_data_alloc)) {
+ VATTR_CLEAR_ACTIVE(vap, va_data_alloc);
+ }
+
+ /*
+ * If we don't have an encoding, go with UTF-8
+ */
+ if ((alp->commonattr & ATTR_CMN_SCRIPT) &&
+ !VATTR_IS_SUPPORTED(vap, va_encoding) && !return_valid) {
+ VATTR_RETURN(vap, va_encoding,
+ 0x7e /* kTextEncodingMacUnicode */);
+ }
+
+ /*
+ * If we don't have a name, we'll get one from the vnode or
+ * mount point.
+ */
+ if ((alp->commonattr & ATTR_CMN_NAME) &&
+ !VATTR_IS_SUPPORTED(vap, va_name)) {
+ VATTR_CLEAR_ACTIVE(vap, va_name);
+ }
+
+ /* If va_dirlinkcount isn't supported use a default of 1. */
+ if ((alp->dirattr & ATTR_DIR_LINKCOUNT) &&
+ !VATTR_IS_SUPPORTED(vap, va_dirlinkcount)) {
+ VATTR_RETURN(vap, va_dirlinkcount, 1);
+ }
+}
+
+static errno_t
+calc_varsize(vnode_t vp, struct attrlist *alp, struct vnode_attr *vap,
+ ssize_t *varsizep, char *fullpathptr, ssize_t *fullpathlenp,
+ char *relpathptr, ssize_t *relpathlenp, const char **vnamep,
+ const char **cnpp, ssize_t *cnlp)
+{
+ int error = 0;
+
+ *varsizep = 0; /* length count */
+ /* We may need to fix up the name attribute if requested */
+ if (alp->commonattr & ATTR_CMN_NAME) {
+ if (VATTR_IS_SUPPORTED(vap, va_name)) {
+ vap->va_name[MAXPATHLEN-1] = '\0'; /* Ensure nul-termination */
+ *cnpp = vap->va_name;
+ *cnlp = strlen(*cnpp);
+ } else if (vp) {
+ /* Filesystem did not support getting the name */
+ if (vnode_isvroot(vp)) {
+ if (vp->v_mount->mnt_vfsstat.f_mntonname[1] == 0x00 &&
+ vp->v_mount->mnt_vfsstat.f_mntonname[0] == '/') {
+ /* special case for boot volume. Use root name when it's
+ * available (which is the volume name) or just the mount on
+ * name of "/". we must do this for binary compatibility with
+ * pre Tiger code. returning nothing for the boot volume name
+ * breaks installers - 3961058
+ */
+ *cnpp = *vnamep = vnode_getname(vp);
+ if (*cnpp == NULL) {
+ /* just use "/" as name */
+ *cnpp = &vp->v_mount->mnt_vfsstat.f_mntonname[0];
+ }
+ *cnlp = strlen(*cnpp);
+ }
+ else {
+ getattrlist_findnamecomp(vp->v_mount->mnt_vfsstat.f_mntonname, cnpp, cnlp);
+ }
+ }
+ else {
+ *cnpp = *vnamep = vnode_getname(vp);
+ *cnlp = 0;
+ if (*cnpp != NULL) {
+ *cnlp = strlen(*cnpp);
+ }
+ }
+ } else {
+ *cnlp = 0;
+ }
+ *varsizep += roundup(*cnlp + 1, 4);
+ }
+
+ /*
+ * Compute the full path to this vnode, if necessary. This attribute is almost certainly
+ * not supported by any filesystem, so build the path to this vnode at this time.
+ */
+ if (vp && (alp->commonattr & ATTR_CMN_FULLPATH)) {
+ int len = MAXPATHLEN;
+ int err;
+
+ /* call build_path making sure NOT to use the cache-only behavior */
+ err = build_path(vp, fullpathptr, len, &len, 0, vfs_context_current());
+ if (err) {
+ error = err;
+ goto out;
+ }
+ *fullpathlenp = 0;
+ if (fullpathptr){
+ *fullpathlenp = strlen(fullpathptr);
+ }
+ *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.
+ *
+ * XXX This needs to change at some point; since the blob is opaque in
+ * user-space this is OK.
+ */
+ if ((alp->commonattr & ATTR_CMN_EXTENDED_SECURITY) &&
+ VATTR_IS_SUPPORTED(vap, va_acl) &&
+ (vap->va_acl != NULL)) {
+
+ /*
+ * Since we have a kauth_acl_t (not a kauth_filesec_t), we have to check against
+ * KAUTH_FILESEC_NOACL ourselves
+ */
+ if (vap->va_acl->acl_entrycount == KAUTH_FILESEC_NOACL) {
+ *varsizep += roundup((KAUTH_FILESEC_SIZE(0)), 4);
+ }
+ else {
+ *varsizep += roundup ((KAUTH_FILESEC_SIZE(vap->va_acl->acl_entrycount)), 4);
+ }
+ }
+
+out:
+ return (error);
+}
+
+static errno_t
+vfs_attr_pack_internal(vnode_t vp, uio_t auio, struct attrlist *alp,
+ uint64_t options, struct vnode_attr *vap, __unused void *fndesc,
+ vfs_context_t ctx, int is_bulk, enum vtype vtype, ssize_t fixedsize)
+{
+ struct _attrlist_buf ab;
+ ssize_t buf_size;
+ size_t copy_size;
+ ssize_t varsize;
+ const char *vname = NULL;
+ const char *cnp;
+ ssize_t cnl;
+ char *fullpathptr;
+ ssize_t fullpathlen;
+ char *relpathptr;
+ ssize_t relpathlen;
+ 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;
+ cnp = "unknown";
+ cnl = 0;
+ fullpathptr = NULL;
+ fullpathlen = 0;
+ relpathptr = NULL;
+ relpathlen = 0;
+ error = 0;
+ alloc_local_buf = 0;
+
+ buf_size = (ssize_t)uio_resid(auio);
+ if ((buf_size <= 0) || (uio_iovcnt(auio) > 1))
+ return (EINVAL);
+
+ copy_size = 0;
+ /* Check for special packing semantics */
+ return_valid = (alp->commonattr & ATTR_CMN_RETURNED_ATTRS) ? 1 : 0;
+ pack_invalid = (options & FSOPT_PACK_INVAL_ATTRS) ? 1 : 0;
+
+ if (pack_invalid) {
+ /* Generate a valid mask for post processing */
+ bcopy(&(alp->commonattr), &ab.valid, sizeof (attribute_set_t));
+ }
+
+ /* did we ask for something the filesystem doesn't support? */
+ if (vap->va_active && !VATTR_ALL_SUPPORTED(vap)) {
+ vattr_get_alt_data(vp, alp, vap, return_valid, is_bulk,
+ ctx);
+
+ /* check again */
+ if (!VATTR_ALL_SUPPORTED(vap)) {
+ if (return_valid && pack_invalid) {
+ /* Fix up valid mask for post processing */
+ getattrlist_fixupattrs(&ab.valid, vap, use_fork);
+
+ /* Force packing of everything asked for */
+ vap->va_supported = vap->va_active;
+ } else if (return_valid) {
+ /* Adjust the requested attributes */
+ getattrlist_fixupattrs(
+ (attribute_set_t *)&(alp->commonattr), vap, use_fork);
+ } else {
+ error = EINVAL;
+ }
+ }
+
+ if (error)
+ goto out;
+ }
+
+ //if a path is requested, allocate a temporary buffer to build it
+ if (vp && (alp->commonattr & (ATTR_CMN_FULLPATH))) {
+ fullpathptr = (char*) 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,
+ relpathptr, &relpathlen, &vname, &cnp, &cnl);
+ if (error)
+ goto out;
+
+ /*
+ * Allocate a target buffer for attribute results.
+ *
+ * Note that we won't ever copy out more than the caller requested, even though
+ * we might have to allocate more than they offer so that the diagnostic checks
+ * don't result in a panic if the caller's buffer is too small..
+ */
+ ab.allocated = fixedsize + varsize;
+ /* Cast 'allocated' to an unsigned to verify allocation size */
+ if ( ((size_t)ab.allocated) > ATTR_MAX_BUFFER) {
+ error = ENOMEM;
+ VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: buffer size too large (%d limit %d)", ab.allocated, ATTR_MAX_BUFFER);
+ goto out;
+ }
+
+ /*
+ * Special handling for bulk calls, align to 8 (and only if enough
+ * space left.
+ */
+ if (is_bulk) {
+ if (buf_size < ab.allocated) {
+ goto out;
+ } else {
+ uint32_t newlen;
+
+ newlen = (ab.allocated + 7) & ~0x07;
+ /* Align only if enough space for alignment */
+ if (newlen <= (uint32_t)buf_size)
+ ab.allocated = newlen;
+ }
+ }
+
+ /*
+ * See if we can reuse buffer passed in i.e. it is a kernel buffer
+ * and big enough.
+ */
+ if (uio_isuserspace(auio) || (buf_size < ab.allocated)) {
+ MALLOC(ab.base, char *, ab.allocated, M_TEMP,
+ M_ZERO | M_WAITOK);
+ alloc_local_buf = 1;
+ } else {
+ /*
+ * In case this is a kernel buffer and sufficiently
+ * big, this function will try to use that buffer
+ * instead of allocating another buffer and bcopy'ing
+ * into it.
+ *
+ * The calculation below figures out where to start
+ * writing in the buffer and once all the data has been
+ * filled in, uio_resid is updated to reflect the usage
+ * of the buffer.
+ *
+ * uio_offset cannot be used here to determine the
+ * starting location as uio_offset could be set to a
+ * value which has nothing to do the location
+ * in the buffer.
+ */
+ ab.base = (char *)uio_curriovbase(auio) +
+ ((ssize_t)uio_curriovlen(auio) - buf_size);
+ bzero(ab.base, ab.allocated);
+ }
+
+ if (ab.base == NULL) {
+ error = ENOMEM;
+ VFS_DEBUG(ctx, vp, "ATTRLIST - ERROR: could not allocate %d for copy buffer", ab.allocated);
+ goto out;
+ }
+
+
+ /* set the S_IFMT bits for the mode */
+ if (alp->commonattr & ATTR_CMN_ACCESSMASK) {
+ if (vp) {
+ switch (vp->v_type) {
+ case VREG:
+ vap->va_mode |= S_IFREG;
+ break;
+ case VDIR:
+ vap->va_mode |= S_IFDIR;
+ break;
+ case VBLK:
+ vap->va_mode |= S_IFBLK;
+ break;
+ case VCHR:
+ vap->va_mode |= S_IFCHR;
+ break;
+ case VLNK:
+ vap->va_mode |= S_IFLNK;
+ break;
+ case VSOCK:
+ vap->va_mode |= S_IFSOCK;
+ break;
+ case VFIFO:
+ vap->va_mode |= S_IFIFO;
+ break;
+ default:
+ error = EBADF;
+ goto out;
+ }
+ }
+ }
+
+ /*
+ * Pack results into the destination buffer.
+ */
+ ab.fixedcursor = ab.base + sizeof(uint32_t);
+ if (return_valid) {
+ ab.fixedcursor += sizeof (attribute_set_t);
+ bzero(&ab.actual, sizeof (ab.actual));
+ }
+ ab.varcursor = ab.base + fixedsize;
+ ab.needed = ab.allocated;
+
+ /* common attributes ************************************************/
+ error = attr_pack_common(ctx, vp, alp, &ab, vap, proc_is64, cnp, cnl,
+ fullpathptr, fullpathlen, return_valid, pack_invalid, vtype, is_bulk);
+
+ /* directory attributes *********************************************/
+ if (!error && alp->dirattr && (vtype == VDIR)) {
+ error = attr_pack_dir(vp, alp, &ab, vap, return_valid, pack_invalid);
+ }
+
+ /* file attributes **************************************************/
+ if (!error && alp->fileattr && (vtype != VDIR)) {
+ error = attr_pack_file(ctx, vp, alp, &ab, vap, return_valid,
+ pack_invalid, is_bulk);
+ }
+
+ /* common extended attributes *****************************************/
+ if (!error && use_fork) {
+ error = attr_pack_common_extended(vp, alp, &ab, relpathptr, relpathlen,
+ vap, return_valid, pack_invalid);
+ }
+
+ if (error)
+ goto out;
+
+ /* diagnostic */
+ if (!return_valid && (ab.fixedcursor - ab.base) != fixedsize)
+ panic("packed field size mismatch; allocated %ld but packed %ld for common %08x vol %08x",
+ fixedsize, (long) (ab.fixedcursor - ab.base), alp->commonattr, alp->volattr);
+ if (!return_valid && ab.varcursor != (ab.base + ab.needed))
+ panic("packed variable field size mismatch; used %ld but expected %ld", (long) (ab.varcursor - ab.base), ab.needed);
+
+ /*
+ * In the compatible case, we report the smaller of the required and returned sizes.
+ * If the FSOPT_REPORT_FULLSIZE option is supplied, we report the full (required) size
+ * of the result buffer, even if we copied less out. The caller knows how big a buffer
+ * they gave us, so they can always check for truncation themselves.
+ */
+ *(uint32_t *)ab.base = (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));
+ }
+
+ copy_size = imin(buf_size, ab.allocated);
+
+ /* Only actually copyout as much out as the user buffer can hold */
+ if (alloc_local_buf) {
+ error = uiomove(ab.base, copy_size, auio);
+ } else {
+ off_t orig_offset = uio_offset(auio);
+
+ /*
+ * The buffer in the uio struct was used directly
+ * (i.e. it was a kernel buffer and big enough
+ * to hold the data required) in order to avoid
+ * un-needed allocation and copies.
+ *
+ * At this point, update the resid value to what it
+ * would be if this was the result of a uiomove. The
+ * offset is also incremented, though it may not
+ * mean anything to the caller but that is what
+ * uiomove does as well.
+ */
+ uio_setresid(auio, buf_size - copy_size);
+ uio_setoffset(auio, orig_offset + (off_t)copy_size);
+ }
+
+out:
+ if (vname)
+ vnode_putname(vname);
+ if (fullpathptr)
+ kfree(fullpathptr, MAXPATHLEN);
+ if (relpathptr)
+ kfree(relpathptr, MAXPATHLEN);
+ if (ab.base != NULL && alloc_local_buf)
+ FREE(ab.base, M_TEMP);
+ return (error);
+}
+
+errno_t
+vfs_attr_pack(vnode_t vp, uio_t uio, struct attrlist *alp, uint64_t options,
+ struct vnode_attr *vap, __unused void *fndesc, vfs_context_t ctx)
+{
+ int error;
+ ssize_t fixedsize;
+ uint64_t orig_active;
+ struct attrlist orig_al;
+ enum vtype v_type;
+
+ if (vp)
+ v_type = vnode_vtype(vp);
+ else
+ v_type = vap->va_objtype;
+
+ orig_al = *alp;
+ orig_active = vap->va_active;
+ vap->va_active = 0;
+
+ error = getattrlist_setupvattr_all(alp, vap, v_type, &fixedsize,
+ proc_is64bit(vfs_context_proc(ctx)), options & FSOPT_ATTR_CMN_EXTENDED);
+
+ if (error) {
+ VFS_DEBUG(ctx, vp,
+ "ATTRLIST - ERROR: setup for request failed");
+ goto out;
+ }
+
+ error = vfs_attr_pack_internal(vp, uio, alp,
+ options|FSOPT_REPORT_FULLSIZE, vap, NULL, ctx, 1, v_type,
+ fixedsize);
+
+ VATTR_CLEAR_SUPPORTED_ALL(vap);
+ vap->va_active = orig_active;
+ *alp = orig_al;
+out:
+ return (error);
+}
+
+/*
+ * Obtain attribute information about a filesystem object.
+ *
+ * Note: The alt_name parameter can be used by the caller to pass in the vnode
+ * name obtained from some authoritative source (eg. readdir vnop); where
+ * filesystems' getattr vnops do not support ATTR_CMN_NAME, the alt_name will be
+ * used as the ATTR_CMN_NAME attribute returned in vnode_attr.va_name.
+ *
+ */
+static int
+getattrlist_internal(vfs_context_t ctx, vnode_t vp, struct attrlist *alp,
+ user_addr_t attributeBuffer, size_t bufferSize, uint64_t options,
+ enum uio_seg segflg, char* authoritative_name, struct ucred *file_cred)
+{
+ struct vnode_attr va;