+
+ /*
+ * If this was a straight lookup operation, we may need to redrive the entire
+ * lookup starting from cat_lookup if the element was deleted as the result of
+ * a rename operation. Since rename is supposed to guarantee atomicity, then
+ * lookups cannot fail because the underlying element is deleted as a result of
+ * the rename call -- either they returned the looked up element prior to rename
+ * or return the newer element. If we are in this region, then all we can do is add
+ * workarounds to guarantee the latter case. The element has already been deleted, so
+ * we just re-try the lookup to ensure the caller gets the most recent element.
+ */
+ if ((retval == ENOENT) && (cnp->cn_nameiop == LOOKUP) &&
+ (newvnode_flags & (GNV_CHASH_RENAMED | GNV_CAT_DELETED))) {
+ if (dcp) {
+ hfs_unlock (dcp);
+ }
+ /* get rid of any name buffers that may have lingered from the cat_lookup call */
+ cat_releasedesc (&desc);
+ goto retry;
+ }
+
+ /* Also, re-drive the lookup if the item we looked up was a hardlink, and the number
+ * or name of hardlinks has changed in the interim between the cat_lookup above, and
+ * our call to hfs_getnewvnode. hfs_getnewvnode will validate the cattr we passed it
+ * against what is actually in the catalog after the cnode is created. If there were
+ * any issues, it will bubble out ERECYCLE, which we need to swallow and use as the
+ * key to redrive as well. We need to special case this below because in this case,
+ * it needs to occur regardless of the type of lookup we're doing here.
+ */
+ if ((retval == ERECYCLE) && (newvnode_flags & GNV_CAT_ATTRCHANGED)) {
+ if (dcp) {
+ hfs_unlock (dcp);
+ }
+ /* get rid of any name buffers that may have lingered from the cat_lookup call */
+ cat_releasedesc (&desc);
+ retval = 0;
+ goto retry;
+ }
+
+ /* skip to the error-handling code if we can't retry */