All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Darrick J. Wong" <darrick.wong@oracle.com>
To: Christoph Hellwig <hch@lst.de>
Cc: linux-xfs@vger.kernel.org
Subject: Re: [PATCH 3/3] xfs: simplify validation of the unwritten extent bit
Date: Wed, 19 Apr 2017 13:17:51 -0700	[thread overview]
Message-ID: <20170419201751.GM5193@birch.djwong.org> (raw)
In-Reply-To: <20170419192904.22203-4-hch@lst.de>

On Wed, Apr 19, 2017 at 09:29:04PM +0200, Christoph Hellwig wrote:
> XFS only supports the unwritten extent bit in the data fork, and only if
> the file system has a version 5 superblock or the unwritten extent
> feature bit.
> 
> We currently have two routines that validate the invariant:
> xfs_check_nostate_extents which return -EFSCORRUPTED when it's not met,
> and xfs_validate_extent that triggers and assert in debug build.
> 
> Both of them iterate over all extents of an inode fork when called,
> which isn't very efficient.
> 
> This patch instead adds a new helper that verifies the invariant one
> extent at a time, and calls it from the places where we iterate over
> all extents to converted them from or two the in-memory format.  The
> callers then return -EFSCORRUPTED when reading invalid extents from
> disk, or trigger an assert when writing them to disk.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/libxfs/xfs_bmap.c       | 16 +-------
>  fs/xfs/libxfs/xfs_bmap.h       |  2 -
>  fs/xfs/libxfs/xfs_bmap_btree.c | 26 ------------
>  fs/xfs/libxfs/xfs_bmap_btree.h | 21 ++++++----
>  fs/xfs/libxfs/xfs_format.h     |  8 ----
>  fs/xfs/libxfs/xfs_inode_fork.c | 90 ++++++++++++------------------------------
>  6 files changed, 41 insertions(+), 122 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 9bd104f32908..ad5ae571f74f 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -1260,7 +1260,6 @@ xfs_bmap_read_extents(
>  	xfs_fsblock_t		bno;	/* block # of "block" */
>  	xfs_buf_t		*bp;	/* buffer for "block" */
>  	int			error;	/* error return value */
> -	xfs_exntfmt_t		exntf;	/* XFS_EXTFMT_NOSTATE, if checking */
>  	xfs_extnum_t		i, j;	/* index into the extents list */
>  	xfs_ifork_t		*ifp;	/* fork structure */
>  	int			level;	/* btree level, for checking */
> @@ -1271,8 +1270,6 @@ xfs_bmap_read_extents(
>  
>  	mp = ip->i_mount;
>  	ifp = XFS_IFORK_PTR(ip, whichfork);
> -	exntf = (whichfork != XFS_DATA_FORK) ? XFS_EXTFMT_NOSTATE :
> -					XFS_EXTFMT_INODE(ip);
>  	block = ifp->if_broot;
>  	/*
>  	 * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out.
> @@ -1340,18 +1337,9 @@ xfs_bmap_read_extents(
>  			xfs_bmbt_rec_host_t *trp = xfs_iext_get_ext(ifp, i);
>  			trp->l0 = be64_to_cpu(frp->l0);
>  			trp->l1 = be64_to_cpu(frp->l1);
> -		}
> -		if (exntf == XFS_EXTFMT_NOSTATE) {
> -			/*
> -			 * Check all attribute bmap btree records and
> -			 * any "older" data bmap btree records for a
> -			 * set bit in the "extent flag" position.
> -			 */
> -			if (unlikely(xfs_check_nostate_extents(ifp,
> -					start, num_recs))) {
> +			if (!xfs_bmbt_validate_extent(mp, whichfork, trp)) {
>  				XFS_ERROR_REPORT("xfs_bmap_read_extents(2)",
> -						 XFS_ERRLEVEL_LOW,
> -						 ip->i_mount);
> +						 XFS_ERRLEVEL_LOW, mp);
>  				goto error0;
>  			}
>  		}
> diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
> index a6e612cabe15..c35a14fa1527 100644
> --- a/fs/xfs/libxfs/xfs_bmap.h
> +++ b/fs/xfs/libxfs/xfs_bmap.h
> @@ -244,8 +244,6 @@ int	xfs_bmap_del_extent_delay(struct xfs_inode *ip, int whichfork,
>  		struct xfs_bmbt_irec *del);
>  void	xfs_bmap_del_extent_cow(struct xfs_inode *ip, xfs_extnum_t *idx,
>  		struct xfs_bmbt_irec *got, struct xfs_bmbt_irec *del);
> -int	xfs_check_nostate_extents(struct xfs_ifork *ifp, xfs_extnum_t idx,
> -		xfs_extnum_t num);
>  uint	xfs_default_attroffset(struct xfs_inode *ip);
>  int	xfs_bmap_shift_extents(struct xfs_trans *tp, struct xfs_inode *ip,
>  		xfs_fileoff_t *next_fsb, xfs_fileoff_t offset_shift_fsb,
> diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c
> index fd55db479385..a5d34a37f314 100644
> --- a/fs/xfs/libxfs/xfs_bmap_btree.c
> +++ b/fs/xfs/libxfs/xfs_bmap_btree.c
> @@ -366,32 +366,6 @@ xfs_bmbt_to_bmdr(
>  	memcpy(tpp, fpp, sizeof(*fpp) * dmxr);
>  }
>  
> -/*
> - * Check extent records, which have just been read, for
> - * any bit in the extent flag field. ASSERT on debug
> - * kernels, as this condition should not occur.
> - * Return an error condition (1) if any flags found,
> - * otherwise return 0.
> - */
> -
> -int
> -xfs_check_nostate_extents(
> -	xfs_ifork_t		*ifp,
> -	xfs_extnum_t		idx,
> -	xfs_extnum_t		num)
> -{
> -	for (; num > 0; num--, idx++) {
> -		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, idx);
> -		if ((ep->l0 >>
> -		     (64 - BMBT_EXNTFLAG_BITLEN)) != 0) {
> -			ASSERT(0);
> -			return 1;
> -		}
> -	}
> -	return 0;
> -}
> -
> -
>  STATIC struct xfs_btree_cur *
>  xfs_bmbt_dup_cursor(
>  	struct xfs_btree_cur	*cur)
> diff --git a/fs/xfs/libxfs/xfs_bmap_btree.h b/fs/xfs/libxfs/xfs_bmap_btree.h
> index 90347a99c6d2..9da5a8d4f184 100644
> --- a/fs/xfs/libxfs/xfs_bmap_btree.h
> +++ b/fs/xfs/libxfs/xfs_bmap_btree.h
> @@ -25,13 +25,6 @@ struct xfs_inode;
>  struct xfs_trans;
>  
>  /*
> - * Extent state and extent format macros.
> - */
> -#define XFS_EXTFMT_INODE(x)	\
> -	(xfs_sb_version_hasextflgbit(&((x)->i_mount->m_sb)) ? \
> -		XFS_EXTFMT_HASSTATE : XFS_EXTFMT_NOSTATE)
> -
> -/*
>   * Btree block header size depends on a superblock flag.
>   */
>  #define XFS_BMBT_BLOCK_LEN(mp) \
> @@ -139,4 +132,18 @@ extern int xfs_bmbt_change_owner(struct xfs_trans *tp, struct xfs_inode *ip,
>  extern struct xfs_btree_cur *xfs_bmbt_init_cursor(struct xfs_mount *,
>  		struct xfs_trans *, struct xfs_inode *, int);
>  
> +/*
> + * Check that the extent does not contain an invalid unwritten extent flag.
> + */
> +static inline bool xfs_bmbt_validate_extent(struct xfs_mount *mp, int whichfork,
> +		struct xfs_bmbt_rec_host *ep)

Would be nice to have this function formatted the same way as most of
the rest of the xfs functions, even if it is static inline...

> +{
> +	if (ep->l0 >> (64 - BMBT_EXNTFLAG_BITLEN) == 0)
> +		return true;
> +	if (whichfork == XFS_DATA_FORK &&
> +	    xfs_sb_version_hasextflgbit(&mp->m_sb))
> +		return true;
> +	return false;
> +}
> +
>  #endif	/* __XFS_BMAP_BTREE_H__ */
> diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
> index 8425884668a8..a1dccd8d96bc 100644
> --- a/fs/xfs/libxfs/xfs_format.h
> +++ b/fs/xfs/libxfs/xfs_format.h
> @@ -1576,14 +1576,6 @@ static inline xfs_filblks_t startblockval(xfs_fsblock_t x)
>  }
>  
>  /*
> - * Possible extent formats.
> - */
> -typedef enum {
> -	XFS_EXTFMT_NOSTATE = 0,
> -	XFS_EXTFMT_HASSTATE
> -} xfs_exntfmt_t;
> -
> -/*
>   * Possible extent states.
>   */
>  typedef enum {
> diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
> index 8a37efe04de3..0e80f34fe97c 100644
> --- a/fs/xfs/libxfs/xfs_inode_fork.c
> +++ b/fs/xfs/libxfs/xfs_inode_fork.c
> @@ -42,35 +42,6 @@ STATIC int xfs_iformat_local(xfs_inode_t *, xfs_dinode_t *, int, int);
>  STATIC int xfs_iformat_extents(xfs_inode_t *, xfs_dinode_t *, int);
>  STATIC int xfs_iformat_btree(xfs_inode_t *, xfs_dinode_t *, int);
>  
> -#ifdef DEBUG
> -/*
> - * Make sure that the extents in the given memory buffer
> - * are valid.
> - */
> -void
> -xfs_validate_extents(
> -	xfs_ifork_t		*ifp,
> -	int			nrecs,
> -	xfs_exntfmt_t		fmt)
> -{
> -	xfs_bmbt_irec_t		irec;
> -	xfs_bmbt_rec_host_t	rec;
> -	int			i;
> -
> -	for (i = 0; i < nrecs; i++) {
> -		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> -		rec.l0 = get_unaligned(&ep->l0);
> -		rec.l1 = get_unaligned(&ep->l1);
> -		xfs_bmbt_get_all(&rec, &irec);
> -		if (fmt == XFS_EXTFMT_NOSTATE)
> -			ASSERT(irec.br_state == XFS_EXT_NORM);
> -	}
> -}
> -#else /* DEBUG */
> -#define xfs_validate_extents(ifp, nrecs, fmt)
> -#endif /* DEBUG */
> -
> -
>  /*
>   * Move inode type and inode format specific information from the
>   * on-disk inode to the in-core inode.  For fifos, devs, and sockets
> @@ -352,40 +323,33 @@ xfs_iformat_local(
>  }
>  
>  /*
> - * The file consists of a set of extents all
> - * of which fit into the on-disk inode.
> - * If there are few enough extents to fit into
> - * the if_inline_ext, then copy them there.
> - * Otherwise allocate a buffer for them and copy
> - * them into it.  Either way, set if_extents
> - * to point at the extents.
> + * The file consists of a set of extents all of which fit into the on-disk
> + * inode.  If there are few enough extents to fit into the if_inline_ext, then
> + * copy them there.  Otherwise allocate a buffer for them and copy them into it.
> + * Either way, set if_extents to point at the extents.
>   */
>  STATIC int
>  xfs_iformat_extents(
> -	xfs_inode_t	*ip,
> -	xfs_dinode_t	*dip,
> -	int		whichfork)
> +	struct xfs_inode	*ip,
> +	struct xfs_dinode	*dip,
> +	int			whichfork)
>  {
> -	xfs_bmbt_rec_t	*dp;
> -	xfs_ifork_t	*ifp;
> -	int		nex;
> -	int		size;
> -	int		i;
> -
> -	ifp = XFS_IFORK_PTR(ip, whichfork);
> -	nex = XFS_DFORK_NEXTENTS(dip, whichfork);
> -	size = nex * (uint)sizeof(xfs_bmbt_rec_t);
> +	struct xfs_mount	*mp = ip->i_mount;
> +	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
> +	int			nex = XFS_DFORK_NEXTENTS(dip, whichfork);
> +	int			size = nex * sizeof(xfs_bmbt_rec_t);
> +	struct xfs_bmbt_rec	*dp;
> +	int			i;
>  
>  	/*
> -	 * If the number of extents is unreasonable, then something
> -	 * is wrong and we just bail out rather than crash in
> -	 * kmem_alloc() or memcpy() below.
> +	 * If the number of extents is unreasonable, then something is wrong and
> +	 * we just bail out rather than crash in kmem_alloc() or memcpy() below.
>  	 */
> -	if (unlikely(size < 0 || size > XFS_DFORK_SIZE(dip, ip->i_mount, whichfork))) {
> +	if (unlikely(size < 0 || size > XFS_DFORK_SIZE(dip, mp, whichfork))) {
>  		xfs_warn(ip->i_mount, "corrupt inode %Lu ((a)extents = %d).",
>  			(unsigned long long) ip->i_ino, nex);
>  		XFS_CORRUPTION_ERROR("xfs_iformat_extents(1)", XFS_ERRLEVEL_LOW,
> -				     ip->i_mount, dip);
> +				     mp, dip);
>  		return -EFSCORRUPTED;
>  	}
>  
> @@ -400,22 +364,17 @@ xfs_iformat_extents(
>  	ifp->if_bytes = size;
>  	if (size) {
>  		dp = (xfs_bmbt_rec_t *) XFS_DFORK_PTR(dip, whichfork);
> -		xfs_validate_extents(ifp, nex, XFS_EXTFMT_INODE(ip));
>  		for (i = 0; i < nex; i++, dp++) {
>  			xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
>  			ep->l0 = get_unaligned_be64(&dp->l0);
>  			ep->l1 = get_unaligned_be64(&dp->l1);
> +			if (!xfs_bmbt_validate_extent(mp, whichfork, ep)) {
> +				XFS_ERROR_REPORT("xfs_iformat_extents(2)",
> +						 XFS_ERRLEVEL_LOW, mp);
> +				return -EFSCORRUPTED;
> +			}
>  		}
>  		XFS_BMAP_TRACE_EXLIST(ip, nex, whichfork);
> -		if (whichfork != XFS_DATA_FORK ||
> -			XFS_EXTFMT_INODE(ip) == XFS_EXTFMT_NOSTATE)
> -				if (unlikely(xfs_check_nostate_extents(
> -				    ifp, 0, nex))) {
> -					XFS_ERROR_REPORT("xfs_iformat_extents(2)",
> -							 XFS_ERRLEVEL_LOW,
> -							 ip->i_mount);
> -					return -EFSCORRUPTED;
> -				}
>  	}
>  	ifp->if_flags |= XFS_IFEXTENTS;
>  	return 0;
> @@ -518,7 +477,6 @@ xfs_iread_extents(
>  		xfs_iext_destroy(ifp);
>  		return error;
>  	}
> -	xfs_validate_extents(ifp, nextents, XFS_EXTFMT_INODE(ip));
>  	ifp->if_flags |= XFS_IFEXTENTS;
>  	return 0;
>  }
> @@ -837,6 +795,9 @@ xfs_iextents_copy(
>  	copied = 0;
>  	for (i = 0; i < nrecs; i++) {
>  		xfs_bmbt_rec_host_t *ep = xfs_iext_get_ext(ifp, i);
> +
> +		ASSERT(xfs_bmbt_validate_extent(ip->i_mount, whichfork, ep));

Wouldn't this be better off in xfs_iflush_int (like the inline dir
verifier) since we could prevent bad metadata from hitting the disk?
Rather than this, which doesn't do anything on non-debug kernels.

OTOH I guess we don't do any verification of the records getting written
to the bmbt leaves either...

--D

> +
>  		start_block = xfs_bmbt_get_startblock(ep);
>  		if (isnullstartblock(start_block)) {
>  			/*
> @@ -852,7 +813,6 @@ xfs_iextents_copy(
>  		copied++;
>  	}
>  	ASSERT(copied != 0);
> -	xfs_validate_extents(ifp, copied, XFS_EXTFMT_INODE(ip));
>  
>  	return (copied * (uint)sizeof(xfs_bmbt_rec_t));
>  }
> -- 
> 2.11.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

  reply	other threads:[~2017-04-19 20:17 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-04-19 19:29 misc cleanups Christoph Hellwig
2017-04-19 19:29 ` [PATCH 1/3] xfs: remove the unused XFS_MAXLINK_1 define Christoph Hellwig
2017-04-19 19:57   ` Darrick J. Wong
2017-04-19 19:58   ` Eric Sandeen
2017-04-19 19:29 ` [PATCH 2/3] xfs: remove unused values from xfs_exntst_t Christoph Hellwig
2017-04-19 19:59   ` Darrick J. Wong
2017-04-19 19:59   ` Eric Sandeen
2017-04-19 19:29 ` [PATCH 3/3] xfs: simplify validation of the unwritten extent bit Christoph Hellwig
2017-04-19 20:17   ` Darrick J. Wong [this message]
2017-04-20 14:38     ` Christoph Hellwig
2017-04-20 16:56       ` Darrick J. Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170419201751.GM5193@birch.djwong.org \
    --to=darrick.wong@oracle.com \
    --cc=hch@lst.de \
    --cc=linux-xfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.