+#if HFS_COMPRESSION
+ /* we need to inspect the decmpfs state of the file before we take the hfs cnode lock */
+ int compressed = 0;
+ int hide_size = 0;
+ off_t uncompressed_size = -1;
+ if (VATTR_IS_ACTIVE(vap, va_data_size) || VATTR_IS_ACTIVE(vap, va_total_alloc) || VATTR_IS_ACTIVE(vap, va_data_alloc) || VATTR_IS_ACTIVE(vap, va_total_size)) {
+ /* we only care about whether the file is compressed if asked for the uncompressed size */
+ if (VNODE_IS_RSRC(vp)) {
+ /* if it's a resource fork, decmpfs may want us to hide the size */
+ hide_size = hfs_hides_rsrc(ap->a_context, cp, 0);
+ } else {
+ /* if it's a data fork, we need to know if it was compressed so we can report the uncompressed size */
+ compressed = hfs_file_is_compressed(cp, 0);
+ }
+ if ((VATTR_IS_ACTIVE(vap, va_data_size) || VATTR_IS_ACTIVE(vap, va_total_size))) {
+ // if it's compressed
+ if (compressed || (!VNODE_IS_RSRC(vp) && cp->c_decmp && cp->c_decmp->cmp_type >= CMP_MAX)) {
+ if (0 != hfs_uncompressed_size_of_compressed_file(NULL, vp, 0, &uncompressed_size, 0)) {
+ /* failed to get the uncompressed size, we'll check for this later */
+ uncompressed_size = -1;
+ } else {
+ // fake that it's compressed
+ compressed = 1;
+ }
+ }
+ }
+ }
+#endif
+
+ /*
+ * Shortcut for vnode_authorize path. Each of the attributes
+ * in this set is updated atomically so we don't need to take
+ * the cnode lock to access them.
+ */
+ if ((vap->va_active & ~VNODE_ATTR_AUTH) == 0) {
+ /* Make sure file still exists. */
+ if (cp->c_flag & C_NOEXISTS)
+ return (ENOENT);
+
+ vap->va_uid = cp->c_uid;
+ vap->va_gid = cp->c_gid;
+ vap->va_mode = cp->c_mode;
+ vap->va_flags = cp->c_bsdflags;
+ vap->va_supported |= VNODE_ATTR_AUTH & ~VNODE_ATTR_va_acl;
+
+ if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) {
+ vap->va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
+ VATTR_SET_SUPPORTED(vap, va_acl);
+ }
+
+ return (0);
+ }
+
+ hfsmp = VTOHFS(vp);
+ v_type = vnode_vtype(vp);
+ /*
+ * If time attributes are requested and we have cnode times
+ * that require updating, then acquire an exclusive lock on
+ * the cnode before updating the times. Otherwise we can
+ * just acquire a shared lock.
+ */
+ if ((vap->va_active & VNODE_ATTR_TIMES) &&
+ (cp->c_touch_acctime || cp->c_touch_chgtime || cp->c_touch_modtime)) {
+ if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK)))
+ return (error);
+ hfs_touchtimes(hfsmp, cp);
+ }
+ else {
+ if ((error = hfs_lock(cp, HFS_SHARED_LOCK)))
+ return (error);
+ }
+
+ if (v_type == VDIR) {
+ data_size = (cp->c_entries + 2) * AVERAGE_HFSDIRENTRY_SIZE;
+
+ if (VATTR_IS_ACTIVE(vap, va_nlink)) {
+ int nlink;
+
+ /*
+ * For directories, the va_nlink is esentially a count
+ * of the ".." references to a directory plus the "."
+ * reference and the directory itself. So for HFS+ this
+ * becomes the sub-directory count plus two.
+ *
+ * In the absence of a sub-directory count we use the
+ * directory's item count. This will be too high in
+ * most cases since it also includes files.
+ */
+ if ((hfsmp->hfs_flags & HFS_FOLDERCOUNT) &&
+ (cp->c_attr.ca_recflags & kHFSHasFolderCountMask))
+ nlink = cp->c_attr.ca_dircount; /* implied ".." entries */
+ else
+ nlink = cp->c_entries;
+
+ /* Account for ourself and our "." entry */
+ nlink += 2;
+ /* Hide our private directories. */
+ if (cp->c_cnid == kHFSRootFolderID) {
+ if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0) {
+ --nlink;
+ }
+ if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0) {
+ --nlink;
+ }
+ }
+ VATTR_RETURN(vap, va_nlink, (u_int64_t)nlink);
+ }
+ if (VATTR_IS_ACTIVE(vap, va_nchildren)) {
+ int entries;
+
+ entries = cp->c_entries;
+ /* Hide our private files and directories. */
+ if (cp->c_cnid == kHFSRootFolderID) {
+ if (hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid != 0)
+ --entries;
+ if (hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid != 0)
+ --entries;
+ if (hfsmp->jnl || ((hfsmp->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))
+ entries -= 2; /* hide the journal files */
+ }
+ VATTR_RETURN(vap, va_nchildren, entries);
+ }
+ /*
+ * The va_dirlinkcount is the count of real directory hard links.
+ * (i.e. its not the sum of the implied "." and ".." references)
+ */
+ if (VATTR_IS_ACTIVE(vap, va_dirlinkcount)) {
+ VATTR_RETURN(vap, va_dirlinkcount, (uint32_t)cp->c_linkcount);
+ }
+ } else /* !VDIR */ {
+ data_size = VCTOF(vp, cp)->ff_size;
+
+ VATTR_RETURN(vap, va_nlink, (u_int64_t)cp->c_linkcount);
+ if (VATTR_IS_ACTIVE(vap, va_data_alloc)) {
+ u_int64_t blocks;
+
+#if HFS_COMPRESSION
+ if (hide_size) {
+ VATTR_RETURN(vap, va_data_alloc, 0);
+ } else if (compressed) {
+ /* for compressed files, we report all allocated blocks as belonging to the data fork */
+ blocks = cp->c_blocks;
+ VATTR_RETURN(vap, va_data_alloc, blocks * (u_int64_t)hfsmp->blockSize);
+ }
+ else
+#endif
+ {
+ blocks = VCTOF(vp, cp)->ff_blocks;
+ VATTR_RETURN(vap, va_data_alloc, blocks * (u_int64_t)hfsmp->blockSize);
+ }
+ }
+ }
+
+ /* conditional because 64-bit arithmetic can be expensive */
+ if (VATTR_IS_ACTIVE(vap, va_total_size)) {
+ if (v_type == VDIR) {
+ VATTR_RETURN(vap, va_total_size, (cp->c_entries + 2) * AVERAGE_HFSDIRENTRY_SIZE);
+ } else {
+ u_int64_t total_size = ~0ULL;
+ struct cnode *rcp;
+#if HFS_COMPRESSION
+ if (hide_size) {
+ /* we're hiding the size of this file, so just return 0 */
+ total_size = 0;
+ } else if (compressed) {
+ if (uncompressed_size == -1) {
+ /*
+ * We failed to get the uncompressed size above,
+ * so we'll fall back to the standard path below
+ * since total_size is still -1
+ */
+ } else {
+ /* use the uncompressed size we fetched above */
+ total_size = uncompressed_size;
+ }
+ }
+#endif
+ if (total_size == ~0ULL) {
+ if (cp->c_datafork) {
+ total_size = cp->c_datafork->ff_size;
+ }
+
+ if (cp->c_blocks - VTOF(vp)->ff_blocks) {
+ /* We deal with rsrc fork vnode iocount at the end of the function */
+ error = hfs_vgetrsrc(hfsmp, vp, &rvp, TRUE, FALSE);
+ if (error) {
+ /*
+ * Note that we call hfs_vgetrsrc with error_on_unlinked
+ * set to FALSE. This is because we may be invoked via
+ * fstat() on an open-unlinked file descriptor and we must
+ * continue to support access to the rsrc fork until it disappears.
+ * The code at the end of this function will be
+ * responsible for releasing the iocount generated by
+ * hfs_vgetrsrc. This is because we can't drop the iocount
+ * without unlocking the cnode first.
+ */
+ goto out;
+ }
+
+ rcp = VTOC(rvp);
+ if (rcp && rcp->c_rsrcfork) {
+ total_size += rcp->c_rsrcfork->ff_size;
+ }
+ }
+ }
+
+ VATTR_RETURN(vap, va_total_size, total_size);
+ }
+ }
+ if (VATTR_IS_ACTIVE(vap, va_total_alloc)) {
+ if (v_type == VDIR) {
+ VATTR_RETURN(vap, va_total_alloc, 0);
+ } else {
+ VATTR_RETURN(vap, va_total_alloc, (u_int64_t)cp->c_blocks * (u_int64_t)hfsmp->blockSize);
+ }
+ }
+
+ /*
+ * If the VFS wants extended security data, and we know that we
+ * don't have any (because it never told us it was setting any)
+ * then we can return the supported bit and no data. If we do
+ * have extended security, we can just leave the bit alone and
+ * the VFS will use the fallback path to fetch it.
+ */
+ if (VATTR_IS_ACTIVE(vap, va_acl)) {
+ if ((cp->c_attr.ca_recflags & kHFSHasSecurityMask) == 0) {
+ vap->va_acl = (kauth_acl_t) KAUTH_FILESEC_NONE;
+ VATTR_SET_SUPPORTED(vap, va_acl);
+ }
+ }
+ if (VATTR_IS_ACTIVE(vap, va_access_time)) {
+ /* Access times are lazily updated, get current time if needed */
+ if (cp->c_touch_acctime) {
+ struct timeval tv;
+
+ microtime(&tv);
+ vap->va_access_time.tv_sec = tv.tv_sec;
+ } else {
+ vap->va_access_time.tv_sec = cp->c_atime;
+ }
+ vap->va_access_time.tv_nsec = 0;
+ VATTR_SET_SUPPORTED(vap, va_access_time);
+ }
+ vap->va_create_time.tv_sec = cp->c_itime;
+ vap->va_create_time.tv_nsec = 0;
+ vap->va_modify_time.tv_sec = cp->c_mtime;
+ vap->va_modify_time.tv_nsec = 0;
+ vap->va_change_time.tv_sec = cp->c_ctime;
+ vap->va_change_time.tv_nsec = 0;
+ vap->va_backup_time.tv_sec = cp->c_btime;
+ vap->va_backup_time.tv_nsec = 0;
+
+ /* See if we need to emit the date added field to the user */
+ if (VATTR_IS_ACTIVE(vap, va_addedtime)) {
+ u_int32_t dateadded = hfs_get_dateadded (cp);
+ if (dateadded) {
+ vap->va_addedtime.tv_sec = dateadded;
+ vap->va_addedtime.tv_nsec = 0;
+ VATTR_SET_SUPPORTED (vap, va_addedtime);
+ }
+ }
+
+ /* XXX is this really a good 'optimal I/O size'? */
+ vap->va_iosize = hfsmp->hfs_logBlockSize;
+ vap->va_uid = cp->c_uid;
+ vap->va_gid = cp->c_gid;
+ vap->va_mode = cp->c_mode;
+ vap->va_flags = cp->c_bsdflags;
+
+ /*
+ * Exporting file IDs from HFS Plus: