Skip to content

Commit

Permalink
Merge tag 'xfs-5.17-merge-3' of git://git.kernel.org/pub/scm/fs/xfs/x…
Browse files Browse the repository at this point in the history
…fs-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
  • Loading branch information
torvalds committed Jan 15, 2022
2 parents 112450d + 4a9bca8 commit a33f5c3
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 46 deletions.
53 changes: 26 additions & 27 deletions fs/xfs/scrub/agheader.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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))
/* compat features must match */
if (sb->sb_features_compat !=
cpu_to_be32(mp->m_sb.sb_features_compat))
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))
/* 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);

/* 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);
/*
* 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);

/* 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))
/* 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))
Expand Down
12 changes: 12 additions & 0 deletions fs/xfs/scrub/agheader_repair.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
53 changes: 34 additions & 19 deletions fs/xfs/xfs_dir2_readdir.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,15 @@ 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 */
int error; /* error return value */
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;

Expand All @@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -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 */

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -507,29 +511,40 @@ 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);

if (xfs_is_shutdown(dp->i_mount))
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;
args.geo = dp->i_mount->m_dir_geo;
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;
}

0 comments on commit a33f5c3

Please sign in to comment.