diff options
Diffstat (limited to 'fs/xfs/scrub/dir.c')
| -rw-r--r-- | fs/xfs/scrub/dir.c | 44 | 
1 files changed, 35 insertions, 9 deletions
| diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c index 69e1efdd4019..50b6a26b0299 100644 --- a/fs/xfs/scrub/dir.c +++ b/fs/xfs/scrub/dir.c @@ -92,7 +92,7 @@ xfs_scrub_dir_check_ftype(  	 * inodes can trigger immediate inactive cleanup of the inode.  	 */  	error = xfs_iget(mp, sdc->sc->tp, inum, 0, 0, &ip); -	if (!xfs_scrub_fblock_process_error(sdc->sc, XFS_DATA_FORK, offset, +	if (!xfs_scrub_fblock_xref_process_error(sdc->sc, XFS_DATA_FORK, offset,  			&error))  		goto out; @@ -200,6 +200,7 @@ xfs_scrub_dir_rec(  	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; @@ -237,9 +238,37 @@ xfs_scrub_dir_rec(  		xfs_scrub_fblock_set_corrupt(ds->sc, XFS_DATA_FORK, rec_bno);  		goto out;  	} +	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)); @@ -324,6 +353,7 @@ xfs_scrub_directory_data_bestfree(  	}  	if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))  		goto out; +	xfs_scrub_buffer_recheck(sc, bp);  	/* XXX: Check xfs_dir3_data_hdr.pad is zero once we start setting it. */ @@ -361,13 +391,7 @@ xfs_scrub_directory_data_bestfree(  	/* 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) { @@ -474,6 +498,7 @@ xfs_scrub_directory_leaf1_bestfree(  	error = xfs_dir3_leaf_read(sc->tp, sc->ip, lblk, -1, &bp);  	if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))  		goto out; +	xfs_scrub_buffer_recheck(sc, bp);  	leaf = bp->b_addr;  	d_ops->leaf_hdr_from_disk(&leafhdr, leaf); @@ -559,6 +584,7 @@ xfs_scrub_directory_free_bestfree(  	error = xfs_dir2_free_read(sc->tp, sc->ip, lblk, &bp);  	if (!xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, lblk, &error))  		goto out; +	xfs_scrub_buffer_recheck(sc, bp);  	if (xfs_sb_version_hascrc(&sc->mp->m_sb)) {  		struct xfs_dir3_free_hdr	*hdr3 = bp->b_addr; |