Commit a33f5c38 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'xfs-5.17-merge-3' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs fixes from Darrick Wong:
 "These are the last few obvious fixes that I found while stress testing
  online fsck for XFS prior to initiating a design review of the whole
  giant machinery.

   - Fix a minor locking inconsistency in readdir

   - Fix incorrect fs feature bit validation for secondary superblocks"

* tag 'xfs-5.17-merge-3' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: fix online fsck handling of v5 feature bits on secondary supers
  xfs: take the ILOCK when readdir inspects directory mapping data
parents 112450df 4a9bca86
Loading
Loading
Loading
Loading
+26 −27
Original line number Diff line number Diff line
@@ -281,7 +281,7 @@ xchk_superblock(
	features_mask = cpu_to_be32(XFS_SB_VERSION2_ATTR2BIT);
	if ((sb->sb_features2 & features_mask) !=
	    (cpu_to_be32(mp->m_sb.sb_features2) & features_mask))
		xchk_block_set_corrupt(sc, bp);
		xchk_block_set_preen(sc, bp);

	if (!xfs_has_crc(mp)) {
		/* all v5 fields must be zero */
@@ -290,39 +290,38 @@ xchk_superblock(
				offsetof(struct xfs_dsb, sb_features_compat)))
			xchk_block_set_corrupt(sc, bp);
	} else {
		/* Check compat flags; all are set at mkfs time. */
		features_mask = cpu_to_be32(XFS_SB_FEAT_COMPAT_UNKNOWN);
		if ((sb->sb_features_compat & features_mask) !=
		    (cpu_to_be32(mp->m_sb.sb_features_compat) & features_mask))
			xchk_block_set_corrupt(sc, bp);

		/* Check ro compat flags; all are set at mkfs time. */
		features_mask = cpu_to_be32(XFS_SB_FEAT_RO_COMPAT_UNKNOWN |
					    XFS_SB_FEAT_RO_COMPAT_FINOBT |
					    XFS_SB_FEAT_RO_COMPAT_RMAPBT |
					    XFS_SB_FEAT_RO_COMPAT_REFLINK);
		if ((sb->sb_features_ro_compat & features_mask) !=
		    (cpu_to_be32(mp->m_sb.sb_features_ro_compat) &
		     features_mask))
			xchk_block_set_corrupt(sc, bp);

		/* Check incompat flags; all are set at mkfs time. */
		features_mask = cpu_to_be32(XFS_SB_FEAT_INCOMPAT_UNKNOWN |
					    XFS_SB_FEAT_INCOMPAT_FTYPE |
					    XFS_SB_FEAT_INCOMPAT_SPINODES |
					    XFS_SB_FEAT_INCOMPAT_META_UUID);
		if ((sb->sb_features_incompat & features_mask) !=
		    (cpu_to_be32(mp->m_sb.sb_features_incompat) &
		     features_mask))
			xchk_block_set_corrupt(sc, bp);

		/* Check log incompat flags; all are set at mkfs time. */
		features_mask = cpu_to_be32(XFS_SB_FEAT_INCOMPAT_LOG_UNKNOWN);
		if ((sb->sb_features_log_incompat & features_mask) !=
		    (cpu_to_be32(mp->m_sb.sb_features_log_incompat) &
		     features_mask))
		/* compat features must match */
		if (sb->sb_features_compat !=
				cpu_to_be32(mp->m_sb.sb_features_compat))
			xchk_block_set_corrupt(sc, bp);

		/* ro compat features must match */
		if (sb->sb_features_ro_compat !=
				cpu_to_be32(mp->m_sb.sb_features_ro_compat))
			xchk_block_set_corrupt(sc, bp);

		/*
		 * NEEDSREPAIR is ignored on a secondary super, so we should
		 * clear it when we find it, though it's not a corruption.
		 */
		features_mask = cpu_to_be32(XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR);
		if ((cpu_to_be32(mp->m_sb.sb_features_incompat) ^
				sb->sb_features_incompat) & features_mask)
			xchk_block_set_preen(sc, bp);

		/* all other incompat features must match */
		if ((cpu_to_be32(mp->m_sb.sb_features_incompat) ^
				sb->sb_features_incompat) & ~features_mask)
			xchk_block_set_corrupt(sc, bp);

		/*
		 * log incompat features protect newer log record types from
		 * older log recovery code.  Log recovery doesn't check the
		 * secondary supers, so we can clear these if needed.
		 */
		if (sb->sb_features_log_incompat)
			xchk_block_set_preen(sc, bp);

		/* Don't care about sb_crc */

		if (sb->sb_spino_align != cpu_to_be32(mp->m_sb.sb_spino_align))
+12 −0
Original line number Diff line number Diff line
@@ -52,6 +52,18 @@ xrep_superblock(
	xfs_buf_zero(bp, 0, BBTOB(bp->b_length));
	xfs_sb_to_disk(bp->b_addr, &mp->m_sb);

	/*
	 * Don't write out a secondary super with NEEDSREPAIR or log incompat
	 * features set, since both are ignored when set on a secondary.
	 */
	if (xfs_has_crc(mp)) {
		struct xfs_dsb		*sb = bp->b_addr;

		sb->sb_features_incompat &=
				~cpu_to_be32(XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR);
		sb->sb_features_log_incompat = 0;
	}

	/* Write this to disk. */
	xfs_trans_buf_set_type(sc->tp, bp, XFS_BLFT_SB_BUF);
	xfs_trans_log_buf(sc->tp, bp, 0, BBTOB(bp->b_length) - 1);
+34 −19
Original line number Diff line number Diff line
@@ -138,7 +138,8 @@ xfs_dir2_sf_getdents(
STATIC int
xfs_dir2_block_getdents(
	struct xfs_da_args	*args,
	struct dir_context	*ctx)
	struct dir_context	*ctx,
	unsigned int		*lock_mode)
{
	struct xfs_inode	*dp = args->dp;	/* incore directory inode */
	struct xfs_buf		*bp;		/* buffer for block */
@@ -146,7 +147,6 @@ xfs_dir2_block_getdents(
	int			wantoff;	/* starting block offset */
	xfs_off_t		cook;
	struct xfs_da_geometry	*geo = args->geo;
	int			lock_mode;
	unsigned int		offset, next_offset;
	unsigned int		end;

@@ -156,12 +156,13 @@ xfs_dir2_block_getdents(
	if (xfs_dir2_dataptr_to_db(geo, ctx->pos) > geo->datablk)
		return 0;

	lock_mode = xfs_ilock_data_map_shared(dp);
	error = xfs_dir3_block_read(args->trans, dp, &bp);
	xfs_iunlock(dp, lock_mode);
	if (error)
		return error;

	xfs_iunlock(dp, *lock_mode);
	*lock_mode = 0;

	/*
	 * Extract the byte offset we start at from the seek pointer.
	 * We'll skip entries before this.
@@ -344,7 +345,8 @@ STATIC int
xfs_dir2_leaf_getdents(
	struct xfs_da_args	*args,
	struct dir_context	*ctx,
	size_t			bufsize)
	size_t			bufsize,
	unsigned int		*lock_mode)
{
	struct xfs_inode	*dp = args->dp;
	struct xfs_mount	*mp = dp->i_mount;
@@ -356,7 +358,6 @@ xfs_dir2_leaf_getdents(
	xfs_dir2_off_t		curoff;		/* current overall offset */
	int			length;		/* temporary length value */
	int			byteoff;	/* offset in current block */
	int			lock_mode;
	unsigned int		offset = 0;
	int			error = 0;	/* error return value */

@@ -390,13 +391,16 @@ xfs_dir2_leaf_getdents(
				bp = NULL;
			}

			lock_mode = xfs_ilock_data_map_shared(dp);
			if (*lock_mode == 0)
				*lock_mode = xfs_ilock_data_map_shared(dp);
			error = xfs_dir2_leaf_readbuf(args, bufsize, &curoff,
					&rablk, &bp);
			xfs_iunlock(dp, lock_mode);
			if (error || !bp)
				break;

			xfs_iunlock(dp, *lock_mode);
			*lock_mode = 0;

			xfs_dir3_data_check(dp, bp);
			/*
			 * Find our position in the block.
@@ -496,7 +500,7 @@ xfs_dir2_leaf_getdents(
 *
 * If supplied, the transaction collects locked dir buffers to avoid
 * nested buffer deadlocks.  This function does not dirty the
 * transaction.  The caller should ensure that the inode is locked
 * transaction.  The caller must hold the IOLOCK (shared or exclusive)
 * before calling this function.
 */
int
@@ -507,8 +511,9 @@ xfs_readdir(
	size_t			bufsize)
{
	struct xfs_da_args	args = { NULL };
	int			rval;
	int			v;
	unsigned int		lock_mode;
	int			isblock;
	int			error;

	trace_xfs_readdir(dp);

@@ -516,6 +521,7 @@ xfs_readdir(
		return -EIO;

	ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
	ASSERT(xfs_isilocked(dp, XFS_IOLOCK_SHARED | XFS_IOLOCK_EXCL));
	XFS_STATS_INC(dp->i_mount, xs_dir_getdents);

	args.dp = dp;
@@ -523,13 +529,22 @@ xfs_readdir(
	args.trans = tp;

	if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
		rval = xfs_dir2_sf_getdents(&args, ctx);
	else if ((rval = xfs_dir2_isblock(&args, &v)))
		;
	else if (v)
		rval = xfs_dir2_block_getdents(&args, ctx);
	else
		rval = xfs_dir2_leaf_getdents(&args, ctx, bufsize);
		return xfs_dir2_sf_getdents(&args, ctx);

	return rval;
	lock_mode = xfs_ilock_data_map_shared(dp);
	error = xfs_dir2_isblock(&args, &isblock);
	if (error)
		goto out_unlock;

	if (isblock) {
		error = xfs_dir2_block_getdents(&args, ctx, &lock_mode);
		goto out_unlock;
	}

	error = xfs_dir2_leaf_getdents(&args, ctx, bufsize, &lock_mode);

out_unlock:
	if (lock_mode)
		xfs_iunlock(dp, lock_mode);
	return error;
}