+ /*
+ * If we get a cnode/vnode pair out of hfs_chash_getcnode, then update the
+ * descriptor in the cnode as needed if the cnode represents a hardlink.
+ * We want the caller to get the most up-to-date copy of the descriptor
+ * as possible. However, we only do anything here if there was a valid vnode.
+ * If there isn't a vnode, then the cnode is brand new and needs to be initialized
+ * as it doesn't have a descriptor or cat_attr yet.
+ *
+ * If we are about to replace the descriptor with the user-supplied one, then validate
+ * that the descriptor correctly acknowledges this item is a hardlink. We could be
+ * subject to a race where the calling thread invoked cat_lookup, got a valid lookup
+ * result but the file was not yet a hardlink. With sufficient delay between there
+ * and here, we might accidentally copy in the raw inode ID into the descriptor in the
+ * call below. If the descriptor's CNID is the same as the fileID then it must
+ * not yet have been a hardlink when the lookup occurred.
+ */
+
+ if (!(hfs_checkdeleted(cp))) {
+ if ((cp->c_flag & C_HARDLINK) && descp->cd_nameptr && descp->cd_namelen > 0) {
+ /* If cnode is uninitialized, its c_attr will be zeroed out; cnids wont match. */
+ if ((descp->cd_cnid == cp->c_attr.ca_fileid) &&
+ (attrp->ca_linkcount != cp->c_attr.ca_linkcount)){
+ if ((flags & GNV_SKIPLOCK) == 0) {
+ /*
+ * Then we took the lock. Drop it before calling
+ * vnode_put, which may invoke hfs_vnop_inactive and need to take
+ * the cnode lock again.
+ */
+ hfs_unlock(cp);
+ }
+
+ /*
+ * Emit ERECYCLE and GNV_CAT_ATTRCHANGED to
+ * force a re-drive in the lookup routine.
+ * Drop the iocount on the vnode obtained from
+ * chash_getcnode if needed.
+ */
+ if (*vpp != NULL) {
+ vnode_put (*vpp);
+ *vpp = NULL;
+ }
+
+ /*
+ * If we raced with VNOP_RECLAIM for this vnode, the hash code could
+ * have observed it after the c_vp or c_rsrc_vp fields had been torn down;
+ * the hash code peeks at those fields without holding the cnode lock because
+ * it needs to be fast. As a result, we may have set H_ATTACH in the chash
+ * call above. Since we're bailing out, unset whatever flags we just set, and
+ * wake up all waiters for this cnode.
+ */
+ if (hflags) {
+ hfs_chashwakeup(hfsmp, cp, hflags);
+ }
+
+ *out_flags = GNV_CAT_ATTRCHANGED;
+ return ERECYCLE;
+ }
+ else {
+ /*
+ * Otherwise, CNID != fileid. Go ahead and copy in the new descriptor.
+ *
+ * Replacing the descriptor here is fine because we looked up the item without
+ * a vnode in hand before. If a vnode existed, its identity must be attached to this
+ * item. We are not susceptible to the lookup fastpath issue at this point.
+ */
+ replace_desc(cp, descp);
+ }
+ }