- VATTR_RETURN(vap, va_fileid, (uint64_t)cp->c_fileid);
- VATTR_RETURN(vap, va_linkid, (uint64_t)cp->c_cnid);
- VATTR_RETURN(vap, va_parentid, (uint64_t)cp->c_parentcnid);
- VATTR_RETURN(vap, va_fsid, cp->c_dev);
- VATTR_RETURN(vap, va_filerev, 0);
-
- VATTR_RETURN(vap, va_encoding, cp->c_encoding);
-
- /* if this is the root, let VFS to find out the mount name, which may be different from the real name */
- if (VATTR_IS_ACTIVE(vap, va_name) && !vnode_isvroot(vp)) {
- /* Return the name for ATTR_CMN_NAME */
- if (cp->c_desc.cd_namelen == 0) {
- error = ENOENT;
- goto out;
+ vap->va_fileid = (u_int64_t)cp->c_fileid;
+ /*
+ * We need to use the origin cache for both hardlinked files
+ * and directories. Hardlinked directories have multiple cnids
+ * and parents (one per link). Hardlinked files also have their
+ * own parents and link IDs separate from the indirect inode number.
+ * If we don't use the cache, we could end up vending the wrong ID
+ * because the cnode will only reflect the link that was looked up most recently.
+ */
+ if (cp->c_flag & C_HARDLINK) {
+ vap->va_linkid = (u_int64_t)hfs_currentcnid(cp);
+ vap->va_parentid = (u_int64_t)hfs_currentparent(cp);
+ } else {
+ vap->va_linkid = (u_int64_t)cp->c_cnid;
+ vap->va_parentid = (u_int64_t)cp->c_parentcnid;
+ }
+ vap->va_fsid = cp->c_dev;
+ vap->va_filerev = 0;
+ vap->va_encoding = cp->c_encoding;
+ vap->va_rdev = (v_type == VBLK || v_type == VCHR) ? cp->c_rdev : 0;
+ vap->va_data_size = data_size;
+
+ /* Mark them all at once instead of individual VATTR_SET_SUPPORTED calls. */
+ vap->va_supported |= VNODE_ATTR_va_create_time | VNODE_ATTR_va_modify_time |
+ VNODE_ATTR_va_change_time| VNODE_ATTR_va_backup_time |
+ VNODE_ATTR_va_iosize | VNODE_ATTR_va_uid |
+ VNODE_ATTR_va_gid | VNODE_ATTR_va_mode |
+ VNODE_ATTR_va_flags |VNODE_ATTR_va_fileid |
+ VNODE_ATTR_va_linkid | VNODE_ATTR_va_parentid |
+ VNODE_ATTR_va_fsid | VNODE_ATTR_va_filerev |
+ VNODE_ATTR_va_encoding | VNODE_ATTR_va_rdev |
+ VNODE_ATTR_va_data_size;
+
+ /* If this is the root, let VFS to find out the mount name, which may be different from the real name.
+ * Otherwise, we need to just take care for hardlinked files, which need to be looked up, if necessary
+ */
+ if (VATTR_IS_ACTIVE(vap, va_name) && (cp->c_cnid != kHFSRootFolderID)) {
+ struct cat_desc linkdesc;
+ int lockflags;
+ int uselinkdesc = 0;
+ cnid_t nextlinkid = 0;
+ cnid_t prevlinkid = 0;
+
+ /* Get the name for ATTR_CMN_NAME. We need to take special care for hardlinks
+ * here because the info. for the link ID requested by getattrlist may be
+ * different than what's currently in the cnode. This is because the cnode
+ * will be filled in with the information for the most recent link ID that went
+ * through namei/lookup(). If there are competing lookups for hardlinks that point
+ * to the same inode, one (or more) getattrlists could be vended incorrect name information.
+ * Also, we need to beware of open-unlinked files which could have a namelen of 0. Note
+ * that if another hardlink sibling of this file is being unlinked, that could also thrash
+ * the name fields but it should *not* be treated like an open-unlinked file here.
+ */
+ if ((cp->c_flag & C_HARDLINK) &&
+ ((cp->c_desc.cd_namelen == 0) || (vap->va_linkid != cp->c_cnid))) {
+ /* If we have no name and our linkID is the raw inode number, then we may
+ * have an open-unlinked file. Go to the next link in this case.
+ */
+ if ((cp->c_desc.cd_namelen == 0) && (vap->va_linkid == cp->c_fileid)) {
+ if ((error = hfs_lookuplink(hfsmp, vap->va_linkid, &prevlinkid, &nextlinkid))) {
+ goto out;
+ }
+ }
+ else {
+ nextlinkid = vap->va_linkid;
+ }
+ /* Now probe the catalog for the linkID. Note that we don't know if we have
+ * the exclusive lock here for the cnode, so we can't just update the descriptor.
+ * Instead, we should just store the descriptor's value locally and then use it to pass
+ * out the name value as needed below.
+ */
+ if (nextlinkid) {
+ lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
+ error = cat_findname(hfsmp, nextlinkid, &linkdesc);
+ hfs_systemfile_unlock(hfsmp, lockflags);
+ if (error == 0) {
+ uselinkdesc = 1;
+ }
+ }