- Fix a minor locking inconsistency in readdir
  - Fix incorrect fs feature bit validation for secondary superblocks
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEUzaAxoMeQq6m2jMV+H93GTRKtOsFAmHfFFYACgkQ+H93GTRK
 tOvrQA//RnDMit4zLoPo3WeZTDjpv1CQkOXFgQSba+Owgz1RvGn5f29R4ICKW6Uy
 NfnPcq5AeNhysOAi0uZPl4EC6140QgMrD4aJjKY1JAc/BkeBVEN05NOUFfCuBXU/
 RMzL8nGD5zsN7fPIWVGS60iitou8IvoX8ky4dKx7XcsbFzbBMtnJIsUpfqVutY9u
 i+zub6sNRkstr4uBRk+1S8uAqHUAW+21YwfKqB6pgpCoO5BHu2e4eN01ohWvF8Ru
 0ujJ2j4YlfjYmtQypFk3rNgQoI0oXY6mYWZPKr7fYvhDpoKEodUvHRLIJEHqoD+y
 fX6Ey5XxpHmDxSJnWRC7Vznl56VEmMndUcWEq2ZqROp/r/zZp7StXyHLO7DR9nEs
 mp+55a4tcKhHa/KnjbqexAaN4a1NTpryMqjsPHP7VTNu5Dq8CK4kHtrcEVrKCZFq
 ExRFMfoUDxap6iJaxoKAz5CtZyJeuZO8bLCa7jq/2F91EWp+2aclxEU6VY5r6B2X
 dFlIY8XnZEfJxE3xnhH/aDs6IKWH4YmvgtwxNb+RIupyMTfJzpcysjV9NRER8fAv
 9rLfWNz+nx0efNyXle+h+vrzT/zXgyi/0PSjAQS9/xErTPRFAmXFefLl/0oMQSHC
 XFIVivcR0lxKpZhB5CIOfVj1Vu8VM52JvlXjHPU3ObqEbFDxtf4=
 =RYJ4
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2022-01-15 07:47:40 +02:00
Родитель 112450df61 4a9bca8680
Коммит a33f5c380c
3 изменённых файлов: 72 добавлений и 46 удалений

Просмотреть файл

@ -281,7 +281,7 @@ xchk_superblock(
features_mask = cpu_to_be32(XFS_SB_VERSION2_ATTR2BIT); features_mask = cpu_to_be32(XFS_SB_VERSION2_ATTR2BIT);
if ((sb->sb_features2 & features_mask) != if ((sb->sb_features2 & features_mask) !=
(cpu_to_be32(mp->m_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)) { if (!xfs_has_crc(mp)) {
/* all v5 fields must be zero */ /* all v5 fields must be zero */
@ -290,38 +290,37 @@ xchk_superblock(
offsetof(struct xfs_dsb, sb_features_compat))) offsetof(struct xfs_dsb, sb_features_compat)))
xchk_block_set_corrupt(sc, bp); xchk_block_set_corrupt(sc, bp);
} else { } else {
/* Check compat flags; all are set at mkfs time. */ /* compat features must match */
features_mask = cpu_to_be32(XFS_SB_FEAT_COMPAT_UNKNOWN); if (sb->sb_features_compat !=
if ((sb->sb_features_compat & features_mask) != cpu_to_be32(mp->m_sb.sb_features_compat))
(cpu_to_be32(mp->m_sb.sb_features_compat) & features_mask))
xchk_block_set_corrupt(sc, bp); xchk_block_set_corrupt(sc, bp);
/* Check ro compat flags; all are set at mkfs time. */ /* ro compat features must match */
features_mask = cpu_to_be32(XFS_SB_FEAT_RO_COMPAT_UNKNOWN | if (sb->sb_features_ro_compat !=
XFS_SB_FEAT_RO_COMPAT_FINOBT | cpu_to_be32(mp->m_sb.sb_features_ro_compat))
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); 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 | * NEEDSREPAIR is ignored on a secondary super, so we should
XFS_SB_FEAT_INCOMPAT_FTYPE | * clear it when we find it, though it's not a corruption.
XFS_SB_FEAT_INCOMPAT_SPINODES | */
XFS_SB_FEAT_INCOMPAT_META_UUID); features_mask = cpu_to_be32(XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR);
if ((sb->sb_features_incompat & features_mask) != if ((cpu_to_be32(mp->m_sb.sb_features_incompat) ^
(cpu_to_be32(mp->m_sb.sb_features_incompat) & sb->sb_features_incompat) & features_mask)
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); 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); * log incompat features protect newer log record types from
if ((sb->sb_features_log_incompat & features_mask) != * older log recovery code. Log recovery doesn't check the
(cpu_to_be32(mp->m_sb.sb_features_log_incompat) & * secondary supers, so we can clear these if needed.
features_mask)) */
xchk_block_set_corrupt(sc, bp); if (sb->sb_features_log_incompat)
xchk_block_set_preen(sc, bp);
/* Don't care about sb_crc */ /* Don't care about sb_crc */

Просмотреть файл

@ -52,6 +52,18 @@ xrep_superblock(
xfs_buf_zero(bp, 0, BBTOB(bp->b_length)); xfs_buf_zero(bp, 0, BBTOB(bp->b_length));
xfs_sb_to_disk(bp->b_addr, &mp->m_sb); 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. */ /* Write this to disk. */
xfs_trans_buf_set_type(sc->tp, bp, XFS_BLFT_SB_BUF); 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); xfs_trans_log_buf(sc->tp, bp, 0, BBTOB(bp->b_length) - 1);

Просмотреть файл

@ -138,7 +138,8 @@ xfs_dir2_sf_getdents(
STATIC int STATIC int
xfs_dir2_block_getdents( xfs_dir2_block_getdents(
struct xfs_da_args *args, 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_inode *dp = args->dp; /* incore directory inode */
struct xfs_buf *bp; /* buffer for block */ struct xfs_buf *bp; /* buffer for block */
@ -146,7 +147,6 @@ xfs_dir2_block_getdents(
int wantoff; /* starting block offset */ int wantoff; /* starting block offset */
xfs_off_t cook; xfs_off_t cook;
struct xfs_da_geometry *geo = args->geo; struct xfs_da_geometry *geo = args->geo;
int lock_mode;
unsigned int offset, next_offset; unsigned int offset, next_offset;
unsigned int end; unsigned int end;
@ -156,12 +156,13 @@ xfs_dir2_block_getdents(
if (xfs_dir2_dataptr_to_db(geo, ctx->pos) > geo->datablk) if (xfs_dir2_dataptr_to_db(geo, ctx->pos) > geo->datablk)
return 0; return 0;
lock_mode = xfs_ilock_data_map_shared(dp);
error = xfs_dir3_block_read(args->trans, dp, &bp); error = xfs_dir3_block_read(args->trans, dp, &bp);
xfs_iunlock(dp, lock_mode);
if (error) if (error)
return error; return error;
xfs_iunlock(dp, *lock_mode);
*lock_mode = 0;
/* /*
* Extract the byte offset we start at from the seek pointer. * Extract the byte offset we start at from the seek pointer.
* We'll skip entries before this. * We'll skip entries before this.
@ -344,7 +345,8 @@ STATIC int
xfs_dir2_leaf_getdents( xfs_dir2_leaf_getdents(
struct xfs_da_args *args, struct xfs_da_args *args,
struct dir_context *ctx, struct dir_context *ctx,
size_t bufsize) size_t bufsize,
unsigned int *lock_mode)
{ {
struct xfs_inode *dp = args->dp; struct xfs_inode *dp = args->dp;
struct xfs_mount *mp = dp->i_mount; struct xfs_mount *mp = dp->i_mount;
@ -356,7 +358,6 @@ xfs_dir2_leaf_getdents(
xfs_dir2_off_t curoff; /* current overall offset */ xfs_dir2_off_t curoff; /* current overall offset */
int length; /* temporary length value */ int length; /* temporary length value */
int byteoff; /* offset in current block */ int byteoff; /* offset in current block */
int lock_mode;
unsigned int offset = 0; unsigned int offset = 0;
int error = 0; /* error return value */ int error = 0; /* error return value */
@ -390,13 +391,16 @@ xfs_dir2_leaf_getdents(
bp = NULL; 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, error = xfs_dir2_leaf_readbuf(args, bufsize, &curoff,
&rablk, &bp); &rablk, &bp);
xfs_iunlock(dp, lock_mode);
if (error || !bp) if (error || !bp)
break; break;
xfs_iunlock(dp, *lock_mode);
*lock_mode = 0;
xfs_dir3_data_check(dp, bp); xfs_dir3_data_check(dp, bp);
/* /*
* Find our position in the block. * Find our position in the block.
@ -496,7 +500,7 @@ xfs_dir2_leaf_getdents(
* *
* If supplied, the transaction collects locked dir buffers to avoid * If supplied, the transaction collects locked dir buffers to avoid
* nested buffer deadlocks. This function does not dirty the * 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. * before calling this function.
*/ */
int int
@ -507,8 +511,9 @@ xfs_readdir(
size_t bufsize) size_t bufsize)
{ {
struct xfs_da_args args = { NULL }; struct xfs_da_args args = { NULL };
int rval; unsigned int lock_mode;
int v; int isblock;
int error;
trace_xfs_readdir(dp); trace_xfs_readdir(dp);
@ -516,6 +521,7 @@ xfs_readdir(
return -EIO; return -EIO;
ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); 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); XFS_STATS_INC(dp->i_mount, xs_dir_getdents);
args.dp = dp; args.dp = dp;
@ -523,13 +529,22 @@ xfs_readdir(
args.trans = tp; args.trans = tp;
if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_getdents(&args, ctx); return 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 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;
} }