struct ovl_path *stack = &origin;
        struct dentry *dentry = NULL;
        struct dentry *index = NULL;
-       struct inode *inode = NULL;
-       bool is_deleted = false;
+       struct inode *inode;
        int err;
 
-       /* First lookup indexed upper by fh */
+       /* First lookup overlay inode in inode cache by origin fh */
+       err = ovl_check_origin_fh(ofs, fh, false, NULL, &stack);
+       if (err)
+               return ERR_PTR(err);
+
+       if (!d_is_dir(origin.dentry) ||
+           !(origin.dentry->d_flags & DCACHE_DISCONNECTED)) {
+               inode = ovl_lookup_inode(sb, origin.dentry, false);
+               err = PTR_ERR(inode);
+               if (IS_ERR(inode))
+                       goto out_err;
+               if (inode) {
+                       dentry = d_find_any_alias(inode);
+                       iput(inode);
+                       if (dentry)
+                               goto out;
+               }
+       }
+
+       /* Then lookup indexed upper/whiteout by origin fh */
        if (ofs->indexdir) {
                index = ovl_get_index_fh(ofs, fh);
                err = PTR_ERR(index);
                if (IS_ERR(index)) {
-                       if (err != -ESTALE)
-                               return ERR_PTR(err);
-
-                       /* Found a whiteout index - treat as deleted inode */
-                       is_deleted = true;
                        index = NULL;
+                       goto out_err;
                }
        }
 
-       /* Then try to get upper dir by index */
+       /* Then try to get a connected upper dir by index */
        if (index && d_is_dir(index)) {
                struct dentry *upper = ovl_index_upper(ofs, index);
 
                goto out;
        }
 
-       /* Then lookup origin by fh */
-       err = ovl_check_origin_fh(ofs, fh, true, NULL, &stack);
-       if (err) {
-               goto out_err;
-       } else if (index) {
-               err = ovl_verify_origin(index, origin.dentry, false);
+       /* Otherwise, get a connected non-upper dir or disconnected non-dir */
+       if (d_is_dir(origin.dentry) &&
+           (origin.dentry->d_flags & DCACHE_DISCONNECTED)) {
+               dput(origin.dentry);
+               origin.dentry = NULL;
+               err = ovl_check_origin_fh(ofs, fh, true, NULL, &stack);
                if (err)
                        goto out_err;
-       } else if (is_deleted) {
-               /* Lookup deleted non-dir by origin inode */
-               if (!d_is_dir(origin.dentry))
-                       inode = ovl_lookup_inode(sb, origin.dentry, false);
-               err = -ESTALE;
-               if (!inode || atomic_read(&inode->i_count) == 1)
+       }
+       if (index) {
+               err = ovl_verify_origin(index, origin.dentry, false);
+               if (err)
                        goto out_err;
-
-               /* Deleted but still open? */
-               index = dget(ovl_i_dentry_upper(inode));
        }
 
        dentry = ovl_get_dentry(sb, NULL, &origin, index);
 out:
        dput(origin.dentry);
        dput(index);
-       iput(inode);
        return dentry;
 
 out_err: