#define XFS_READDIR_BUFSIZE    (32768)
 
 unsigned char xfs_dir3_get_dtype(struct xfs_mount *mp, uint8_t filetype);
+void *xfs_dir3_data_endp(struct xfs_da_geometry *geo,
+               struct xfs_dir2_data_hdr *hdr);
 
 #endif /* __XFS_DIR2_H__ */
 
        case cpu_to_be32(XFS_DIR2_BLOCK_MAGIC):
                btp = xfs_dir2_block_tail_p(geo, hdr);
                lep = xfs_dir2_block_leaf_p(btp);
-               endp = (char *)lep;
 
                /*
                 * The number of leaf entries is limited by the size of the
                break;
        case cpu_to_be32(XFS_DIR3_DATA_MAGIC):
        case cpu_to_be32(XFS_DIR2_DATA_MAGIC):
-               endp = (char *)hdr + geo->blksize;
                break;
        default:
                return __this_address;
        }
+       endp = xfs_dir3_data_endp(geo, hdr);
+       if (!endp)
+               return __this_address;
 
        /*
         * Account for zero bestfree entries.
        struct xfs_dir2_data_hdr *hdr,
        int                     *loghead)
 {
-       xfs_dir2_block_tail_t   *btp;           /* block tail */
        xfs_dir2_data_entry_t   *dep;           /* active data entry */
        xfs_dir2_data_unused_t  *dup;           /* unused data entry */
        struct xfs_dir2_data_free *bf;
         * Set up pointers.
         */
        p = (char *)ops->data_entry_p(hdr);
-       if (hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
-           hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC)) {
-               btp = xfs_dir2_block_tail_p(geo, hdr);
-               endp = (char *)xfs_dir2_block_leaf_p(btp);
-       } else
-               endp = (char *)hdr + geo->blksize;
+       endp = xfs_dir3_data_endp(geo, hdr);
        /*
         * Loop over the block's entries.
         */
        /*
         * Figure out where the end of the data area is.
         */
-       if (hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
-           hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC))
-               endptr = (char *)hdr + args->geo->blksize;
-       else {
-               xfs_dir2_block_tail_t   *btp;   /* block tail */
+       endptr = xfs_dir3_data_endp(args->geo, hdr);
+       ASSERT(endptr != NULL);
 
-               ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_BLOCK_MAGIC) ||
-                       hdr->magic == cpu_to_be32(XFS_DIR3_BLOCK_MAGIC));
-               btp = xfs_dir2_block_tail_p(args->geo, hdr);
-               endptr = (char *)xfs_dir2_block_leaf_p(btp);
-       }
        /*
         * If this isn't the start of the block, then back up to
         * the previous entry and see if it's free.
        }
        *needscanp = needscan;
 }
+
+/* Find the end of the entry data in a data/block format dir block. */
+void *
+xfs_dir3_data_endp(
+       struct xfs_da_geometry          *geo,
+       struct xfs_dir2_data_hdr        *hdr)
+{
+       switch (hdr->magic) {
+       case cpu_to_be32(XFS_DIR3_BLOCK_MAGIC):
+       case cpu_to_be32(XFS_DIR2_BLOCK_MAGIC):
+               return xfs_dir2_block_leaf_p(xfs_dir2_block_tail_p(geo, hdr));
+       case cpu_to_be32(XFS_DIR3_DATA_MAGIC):
+       case cpu_to_be32(XFS_DIR2_DATA_MAGIC):
+               return (char *)hdr + geo->blksize;
+       default:
+               return NULL;
+       }
+}
 
        xfs_dir2_sf_hdr_t       *sfhp)          /* shortform directory hdr */
 {
        xfs_dir2_data_hdr_t     *hdr;           /* block header */
-       xfs_dir2_block_tail_t   *btp;           /* block tail pointer */
        xfs_dir2_data_entry_t   *dep;           /* data entry pointer */
        xfs_inode_t             *dp;            /* incore directory inode */
        xfs_dir2_data_unused_t  *dup;           /* unused data pointer */
        /*
         * Set up to loop over the block's entries.
         */
-       btp = xfs_dir2_block_tail_p(args->geo, hdr);
        ptr = (char *)dp->d_ops->data_entry_p(hdr);
-       endptr = (char *)xfs_dir2_block_leaf_p(btp);
+       endptr = xfs_dir3_data_endp(args->geo, hdr);
        sfep = xfs_dir2_sf_firstentry(sfp);
        /*
         * Loop over the active and unused entries.
 
        struct xfs_inode                *dp = ds->dargs.dp;
        struct xfs_dir2_data_entry      *dent;
        struct xfs_buf                  *bp;
+       char                            *p, *endp;
        xfs_ino_t                       ino;
        xfs_dablk_t                     rec_bno;
        xfs_dir2_db_t                   db;
        }
        xfs_scrub_buffer_recheck(ds->sc, bp);
 
-       /* Retrieve the entry, sanity check it, and compare hashes. */
        dent = (struct xfs_dir2_data_entry *)(((char *)bp->b_addr) + off);
+
+       /* Make sure we got a real directory entry. */
+       p = (char *)mp->m_dir_inode_ops->data_entry_p(bp->b_addr);
+       endp = xfs_dir3_data_endp(mp->m_dir_geo, bp->b_addr);
+       if (!endp) {
+               xfs_scrub_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
+               goto out_relse;
+       }
+       while (p < endp) {
+               struct xfs_dir2_data_entry      *dep;
+               struct xfs_dir2_data_unused     *dup;
+
+               dup = (struct xfs_dir2_data_unused *)p;
+               if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
+                       p += be16_to_cpu(dup->length);
+                       continue;
+               }
+               dep = (struct xfs_dir2_data_entry *)p;
+               if (dep == dent)
+                       break;
+               p += mp->m_dir_inode_ops->data_entsize(dep->namelen);
+       }
+       if (p >= endp) {
+               xfs_scrub_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);
+               goto out_relse;
+       }
+
+       /* Retrieve the entry, sanity check it, and compare hashes. */
        ino = be64_to_cpu(dent->inumber);
        hash = be32_to_cpu(ent->hashval);
        tag = be16_to_cpup(dp->d_ops->data_entry_tag_p(dent));
 
        /* Make sure the bestfrees are actually the best free spaces. */
        ptr = (char *)d_ops->data_entry_p(bp->b_addr);
-       if (is_block) {
-               struct xfs_dir2_block_tail      *btp;
-
-               btp = xfs_dir2_block_tail_p(mp->m_dir_geo, bp->b_addr);
-               endptr = (char *)xfs_dir2_block_leaf_p(btp);
-       } else
-               endptr = (char *)bp->b_addr + BBTOB(bp->b_length);
+       endptr = xfs_dir3_data_endp(mp->m_dir_geo, bp->b_addr);
 
        /* Iterate the entries, stopping when we hit or go past the end. */
        while (ptr < endptr) {
 
        struct xfs_inode        *dp = args->dp; /* incore directory inode */
        xfs_dir2_data_hdr_t     *hdr;           /* block header */
        struct xfs_buf          *bp;            /* buffer for block */
-       xfs_dir2_block_tail_t   *btp;           /* block tail */
        xfs_dir2_data_entry_t   *dep;           /* block data entry */
        xfs_dir2_data_unused_t  *dup;           /* block unused entry */
        char                    *endptr;        /* end of the data entries */
        /*
         * Set up values for the loop.
         */
-       btp = xfs_dir2_block_tail_p(geo, hdr);
        ptr = (char *)dp->d_ops->data_entry_p(hdr);
-       endptr = (char *)xfs_dir2_block_leaf_p(btp);
+       endptr = xfs_dir3_data_endp(geo, hdr);
 
        /*
         * Loop over the data portion of the block.