[PATCH] xfs: Allow scrub to detect inodes with non-maximal sized extents

Chandan Babu R
2021-02-23T08:27:29Z

This commit now makes it possible for scrub to check if an inode's extents are
maximally sized i.e. it checks if an inode's extent is contiguous (in terms of
both file offset and disk offset) with neighbouring extents and the total
length of both the extents is less than the maximum allowed extent
length (i.e. MAXEXTLEN).

Signed-off-by: Chandan Babu R <chandanrlinux@gmail.com>
---
fs/xfs/scrub/inode.c | 11 +++++++----
fs/xfs/xfs_bmap_util.c | 36 +++++++++++++++++++++++++++++++-----
fs/xfs/xfs_bmap_util.h | 5 +++--
fs/xfs/xfs_qm.c | 2 +-
4 files changed, 42 insertions(+), 12 deletions(-)

diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index faf65eb5bd31..33b530785e36 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -478,23 +478,26 @@ xchk_inode_xref_bmap(
xfs_filblks_t count;
xfs_filblks_t acount;
int error;
+ bool maximal_sized_exts;

if (xchk_skip_xref(sc->sm))
return;

/* Walk all the extents to check nextents/naextents/nblocks. */
+ maximal_sized_exts = true;
error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_DATA_FORK,
- &nextents, &count);
+ &nextents, &count, &maximal_sized_exts);
if (!xchk_should_check_xref(sc, &error, NULL))
return;
- if (nextents < be32_to_cpu(dip->di_nextents))
+ if (nextents < be32_to_cpu(dip->di_nextents) || !maximal_sized_exts)
xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);

+ maximal_sized_exts = true;
error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK,
- &nextents, &acount);
+ &nextents, &acount, &maximal_sized_exts);
if (!xchk_should_check_xref(sc, &error, NULL))
return;
- if (nextents != be16_to_cpu(dip->di_anextents))
+ if (nextents != be16_to_cpu(dip->di_anextents) || !maximal_sized_exts)
xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);

/* Check nblocks against the inode. */
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index e7d68318e6a5..eca39677a46f 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -176,6 +176,25 @@ xfs_bmap_rtalloc(
* Extent tree block counting routines.
*/

+STATIC bool
+is_maximal_extent(
+ struct xfs_ifork *ifp,
+ struct xfs_iext_cursor *icur,
+ struct xfs_bmbt_irec *got)
+{
+ struct xfs_bmbt_irec left;
+
+ if (xfs_iext_peek_prev_extent(ifp, icur, &left) &&
+ !isnullstartblock(left.br_startblock) &&
+ left.br_startoff + left.br_blockcount == got->br_startoff &&
+ left.br_startblock + left.br_blockcount == got->br_startblock &&
+ left.br_state == got->br_state &&
+ left.br_blockcount + got->br_blockcount <= MAXEXTLEN)
+ return false;
+ else
+ return true;
+}
+
/*
* Count leaf blocks given a range of extent records. Delayed allocation
* extents are not counted towards the totals.
@@ -183,7 +202,8 @@ xfs_bmap_rtalloc(
xfs_extnum_t
xfs_bmap_count_leaves(
struct xfs_ifork *ifp,
- xfs_filblks_t *count)
+ xfs_filblks_t *count,
+ bool *maximal_sized_exts)
{
struct xfs_iext_cursor icur;
struct xfs_bmbt_irec got;
@@ -191,6 +211,10 @@ xfs_bmap_count_leaves(

for_each_xfs_iext(ifp, &icur, &got) {
if (!isnullstartblock(got.br_startblock)) {
+ if (maximal_sized_exts)
+ *maximal_sized_exts =
+ is_maximal_extent(ifp, &icur, &got);
+
*count += got.br_blockcount;
numrecs++;
}
@@ -209,7 +233,8 @@ xfs_bmap_count_blocks(
struct xfs_inode *ip,
int whichfork,
xfs_extnum_t *nextents,
- xfs_filblks_t *count)
+ xfs_filblks_t *count,
+ bool *maximal_sized_exts)
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
@@ -246,7 +271,8 @@ xfs_bmap_count_blocks(

/* fall through */
case XFS_DINODE_FMT_EXTENTS:
- *nextents = xfs_bmap_count_leaves(ifp, count);
+ *nextents = xfs_bmap_count_leaves(ifp, count,
+ maximal_sized_exts);
break;
}

@@ -1442,14 +1468,14 @@ xfs_swap_extent_forks(
if (XFS_IFORK_Q(ip) && ip->i_afp->if_nextents > 0 &&
ip->i_afp->if_format != XFS_DINODE_FMT_LOCAL) {
error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK, &junk,
- &aforkblks);
+ &aforkblks, NULL);
if (error)
return error;
}
if (XFS_IFORK_Q(tip) && tip->i_afp->if_nextents > 0 &&
tip->i_afp->if_format != XFS_DINODE_FMT_LOCAL) {
error = xfs_bmap_count_blocks(tp, tip, XFS_ATTR_FORK, &junk,
- &taforkblks);
+ &taforkblks, NULL);
if (error)
return error;
}
diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h
index 9f993168b55b..7b0ce227c7bd 100644
--- a/fs/xfs/xfs_bmap_util.h
+++ b/fs/xfs/xfs_bmap_util.h
@@ -71,10 +71,11 @@ int xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip,

xfs_daddr_t xfs_fsb_to_db(struct xfs_inode *ip, xfs_fsblock_t fsb);

-xfs_extnum_t xfs_bmap_count_leaves(struct xfs_ifork *ifp, xfs_filblks_t *count);
+xfs_extnum_t xfs_bmap_count_leaves(struct xfs_ifork *ifp, xfs_filblks_t *count,
+ bool *maximal_sized_exts);
int xfs_bmap_count_blocks(struct xfs_trans *tp, struct xfs_inode *ip,
int whichfork, xfs_extnum_t *nextents,
- xfs_filblks_t *count);
+ xfs_filblks_t *count, bool *maximal_sized_exts);

int xfs_flush_unmap_range(struct xfs_inode *ip, xfs_off_t offset,
xfs_off_t len);
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 742d1413e2d0..04fcca5583b3 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -1170,7 +1170,7 @@ xfs_qm_dqusage_adjust(
goto error0;
}

- xfs_bmap_count_leaves(ifp, &rtblks);
+ xfs_bmap_count_leaves(ifp, &rtblks, NULL);
}

nblks = (xfs_qcnt_t)ip->i_d.di_nblocks - rtblks;
--
2.29.2