[PATCH V3 06/12] xfs: xfs_dfork_nextents: Return extent count via an out argument

Chandan Babu R
2021-09-16T10:07:46Z

This commit changes xfs_dfork_nextents() to return an error code. The extent
count itself is now returned through an out argument. This facility will be
used by a future commit to indicate an inconsistent ondisk extent count.

Signed-off-by: Chandan Babu R <chandan.babu@oracle.com>
---
fs/xfs/libxfs/xfs_format.h | 14 ++---
fs/xfs/libxfs/xfs_inode_buf.c | 16 ++++--
fs/xfs/libxfs/xfs_inode_fork.c | 21 ++++++--
fs/xfs/scrub/inode.c | 94 +++++++++++++++++++++-------------
fs/xfs/scrub/inode_repair.c | 34 ++++++++----
5 files changed, 118 insertions(+), 61 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index b4638052801f..dba868f2c3e3 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -931,28 +931,30 @@ enum xfs_dinode_fmt {
(dip)->di_format : \
(dip)->di_aformat)

-static inline xfs_extnum_t
+static inline int
xfs_dfork_nextents(
struct xfs_dinode *dip,
- int whichfork)
+ int whichfork,
+ xfs_extnum_t *nextents)
{
- xfs_extnum_t nextents = 0;
+ int error = 0;

switch (whichfork) {
case XFS_DATA_FORK:
- nextents = be32_to_cpu(dip->di_nextents);
+ *nextents = be32_to_cpu(dip->di_nextents);
break;

case XFS_ATTR_FORK:
- nextents = be16_to_cpu(dip->di_anextents);
+ *nextents = be16_to_cpu(dip->di_anextents);
break;

default:
ASSERT(0);
+ error = -EFSCORRUPTED;
break;
}

- return nextents;
+ return error;
}

/*
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index 176c98798aa4..dc511630cc7a 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -345,7 +345,8 @@ xfs_dinode_verify_fork(
xfs_extnum_t di_nextents;
xfs_extnum_t max_extents;

- di_nextents = xfs_dfork_nextents(dip, whichfork);
+ if (xfs_dfork_nextents(dip, whichfork, &di_nextents))
+ return __this_address;

switch (XFS_DFORK_FORMAT(dip, whichfork)) {
case XFS_DINODE_FMT_LOCAL:
@@ -477,6 +478,7 @@ xfs_dinode_verify(
uint64_t flags2;
uint64_t di_size;
xfs_extnum_t nextents;
+ xfs_extnum_t naextents;
xfs_rfsblock_t nblocks;

if (dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC))
@@ -508,8 +510,13 @@ xfs_dinode_verify(
if ((S_ISLNK(mode) || S_ISDIR(mode)) && di_size == 0)
return __this_address;

- nextents = xfs_dfork_nextents(dip, XFS_DATA_FORK);
- nextents += xfs_dfork_nextents(dip, XFS_ATTR_FORK);
+ if (xfs_dfork_nextents(dip, XFS_DATA_FORK, &nextents))
+ return __this_address;
+
+ if (xfs_dfork_nextents(dip, XFS_ATTR_FORK, &naextents))
+ return __this_address;
+
+ nextents += naextents;
nblocks = be64_to_cpu(dip->di_nblocks);

/* Fork checks carried over from xfs_iformat_fork */
@@ -570,7 +577,8 @@ xfs_dinode_verify(
default:
return __this_address;
}
- if (xfs_dfork_nextents(dip, XFS_ATTR_FORK))
+
+ if (naextents)
return __this_address;
}

diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index 7d1efccfea59..435c343612e2 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -107,13 +107,20 @@ xfs_iformat_extents(
struct xfs_mount *mp = ip->i_mount;
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
int state = xfs_bmap_fork_to_state(whichfork);
- xfs_extnum_t nex = xfs_dfork_nextents(dip, whichfork);
- int size = nex * sizeof(xfs_bmbt_rec_t);
+ xfs_extnum_t nex;
+ int size;
struct xfs_iext_cursor icur;
struct xfs_bmbt_rec *dp;
struct xfs_bmbt_irec new;
+ int error;
int i;

+ error = xfs_dfork_nextents(dip, whichfork, &nex);
+ if (error)
+ return error;
+
+ size = nex * sizeof(struct xfs_bmbt_rec);
+
/*
* 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.
@@ -234,7 +241,9 @@ xfs_iformat_data_fork(
* depend on it.
*/
ip->i_df.if_format = dip->di_format;
- ip->i_df.if_nextents = xfs_dfork_nextents(dip, XFS_DATA_FORK);
+ error = xfs_dfork_nextents(dip, XFS_DATA_FORK, &ip->i_df.if_nextents);
+ if (error)
+ return error;

switch (inode->i_mode & S_IFMT) {
case S_IFIFO:
@@ -302,9 +311,11 @@ xfs_iformat_attr_fork(
struct xfs_dinode *dip)
{
xfs_extnum_t naextents;
- int error = 0;
+ int error;

- naextents = xfs_dfork_nextents(dip, XFS_ATTR_FORK);
+ error = xfs_dfork_nextents(dip, XFS_ATTR_FORK, &naextents);
+ if (error)
+ return error;

/*
* Initialize the extent count early, as the per-format routines may
diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c
index 4177b85c941d..be43bd6be1ed 100644
--- a/fs/xfs/scrub/inode.c
+++ b/fs/xfs/scrub/inode.c
@@ -221,6 +221,38 @@ xchk_dinode_nsec(
xchk_ino_set_corrupt(sc, ino);
}

+STATIC void
+xchk_dinode_fork_recs(
+ struct xfs_scrub *sc,
+ struct xfs_dinode *dip,
+ xfs_ino_t ino,
+ xfs_extnum_t nextents,
+ int whichfork)
+{
+ struct xfs_mount *mp = sc->mp;
+ size_t fork_recs;
+ unsigned char format;
+
+ fork_recs = XFS_DFORK_SIZE(dip, mp, whichfork) /
+ sizeof(struct xfs_bmbt_rec);
+ format = XFS_DFORK_FORMAT(dip, whichfork);
+
+ switch (format) {
+ case XFS_DINODE_FMT_EXTENTS:
+ if (nextents > fork_recs)
+ xchk_ino_set_corrupt(sc, ino);
+ break;
+ case XFS_DINODE_FMT_BTREE:
+ if (nextents <= fork_recs)
+ xchk_ino_set_corrupt(sc, ino);
+ break;
+ default:
+ if (nextents != 0)
+ xchk_ino_set_corrupt(sc, ino);
+ break;
+ }
+}
+
/* Scrub all the ondisk inode fields. */
STATIC void
xchk_dinode(
@@ -229,7 +261,6 @@ xchk_dinode(
xfs_ino_t ino)
{
struct xfs_mount *mp = sc->mp;
- size_t fork_recs;
unsigned long long isize;
uint64_t flags2;
xfs_extnum_t nextents;
@@ -237,6 +268,7 @@ xchk_dinode(
prid_t prid;
uint16_t flags;
uint16_t mode;
+ int error;

flags = be16_to_cpu(dip->di_flags);
if (dip->di_version >= 3)
@@ -392,33 +424,30 @@ xchk_dinode(
xchk_inode_extsize(sc, dip, ino, mode, flags);

/* di_nextents */
- nextents = xfs_dfork_nextents(dip, XFS_DATA_FORK);
- fork_recs = XFS_DFORK_DSIZE(dip, mp) / sizeof(struct xfs_bmbt_rec);
- switch (dip->di_format) {
- case XFS_DINODE_FMT_EXTENTS:
- if (nextents > fork_recs)
- xchk_ino_set_corrupt(sc, ino);
- break;
- case XFS_DINODE_FMT_BTREE:
- if (nextents <= fork_recs)
- xchk_ino_set_corrupt(sc, ino);
- break;
- default:
- if (nextents != 0)
- xchk_ino_set_corrupt(sc, ino);
- break;
+ error = xfs_dfork_nextents(dip, XFS_DATA_FORK, &nextents);
+ if (error) {
+ xchk_ino_set_corrupt(sc, ino);
+ return;
}
-
- naextents = xfs_dfork_nextents(dip, XFS_ATTR_FORK);
+ xchk_dinode_fork_recs(sc, dip, ino, nextents, XFS_DATA_FORK);

/* di_forkoff */
if (XFS_DFORK_APTR(dip) >= (char *)dip + mp->m_sb.sb_inodesize)
xchk_ino_set_corrupt(sc, ino);
- if (naextents != 0 && dip->di_forkoff == 0)
- xchk_ino_set_corrupt(sc, ino);
if (dip->di_forkoff == 0 && dip->di_aformat != XFS_DINODE_FMT_EXTENTS)
xchk_ino_set_corrupt(sc, ino);

+ error = xfs_dfork_nextents(dip, XFS_ATTR_FORK, &naextents);
+ if (error) {
+ xchk_ino_set_corrupt(sc, ino);
+ return;
+ }
+
+ if (naextents != 0 && dip->di_forkoff == 0) {
+ xchk_ino_set_corrupt(sc, ino);
+ return;
+ }
+
/* di_aformat */
if (dip->di_aformat != XFS_DINODE_FMT_LOCAL &&
dip->di_aformat != XFS_DINODE_FMT_EXTENTS &&
@@ -426,20 +455,8 @@ xchk_dinode(
xchk_ino_set_corrupt(sc, ino);

/* di_anextents */
- fork_recs = XFS_DFORK_ASIZE(dip, mp) / sizeof(struct xfs_bmbt_rec);
- switch (dip->di_aformat) {
- case XFS_DINODE_FMT_EXTENTS:
- if (naextents > fork_recs)
- xchk_ino_set_corrupt(sc, ino);
- break;
- case XFS_DINODE_FMT_BTREE:
- if (naextents <= fork_recs)
- xchk_ino_set_corrupt(sc, ino);
- break;
- default:
- if (naextents != 0)
- xchk_ino_set_corrupt(sc, ino);
- }
+ if (!error)
+ xchk_dinode_fork_recs(sc, dip, ino, naextents, XFS_ATTR_FORK);

if (dip->di_version >= 3) {
xchk_dinode_nsec(sc, ino, dip, dip->di_crtime);
@@ -502,6 +519,7 @@ xchk_inode_xref_bmap(
struct xfs_scrub *sc,
struct xfs_dinode *dip)
{
+ xfs_extnum_t dip_nextents;
xfs_extnum_t nextents;
xfs_filblks_t count;
xfs_filblks_t acount;
@@ -515,14 +533,18 @@ xchk_inode_xref_bmap(
&nextents, &count);
if (!xchk_should_check_xref(sc, &error, NULL))
return;
- if (nextents < xfs_dfork_nextents(dip, XFS_DATA_FORK))
+
+ error = xfs_dfork_nextents(dip, XFS_DATA_FORK, &dip_nextents);
+ if (error || nextents < dip_nextents)
xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);

error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK,
&nextents, &acount);
if (!xchk_should_check_xref(sc, &error, NULL))
return;
- if (nextents != xfs_dfork_nextents(dip, XFS_ATTR_FORK))
+
+ error = xfs_dfork_nextents(dip, XFS_ATTR_FORK, &dip_nextents);
+ if (error || nextents < dip_nextents)
xchk_ino_xref_set_corrupt(sc, sc->ip->i_ino);

/* Check nblocks against the inode. */
diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c
index ec8360b3b13b..4133a91c9a57 100644
--- a/fs/xfs/scrub/inode_repair.c
+++ b/fs/xfs/scrub/inode_repair.c
@@ -606,7 +606,9 @@ xrep_dinode_bad_extents_fork(
xfs_extnum_t nex;
int fork_size;

- nex = xfs_dfork_nextents(dip, whichfork);
+ if (xfs_dfork_nextents(dip, whichfork, &nex))
+ return true;
+
fork_size = nex * sizeof(struct xfs_bmbt_rec);
if (fork_size < 0 || fork_size > dfork_size)
return true;
@@ -637,11 +639,14 @@ xrep_dinode_bad_btree_fork(
int whichfork)
{
struct xfs_bmdr_block *dfp;
+ xfs_extnum_t nextents;
int nrecs;
int level;

- if (xfs_dfork_nextents(dip, whichfork) <=
- dfork_size / sizeof(struct xfs_bmbt_rec))
+ if (xfs_dfork_nextents(dip, whichfork, &nextents))
+ return true;
+
+ if (nextents <= dfork_size / sizeof(struct xfs_bmbt_rec))
return true;

if (dfork_size < sizeof(struct xfs_bmdr_block))
@@ -778,11 +783,15 @@ xrep_dinode_check_afork(
struct xfs_dinode *dip)
{
struct xfs_attr_shortform *sfp;
+ xfs_extnum_t nextents;
int size;

+ if (xfs_dfork_nextents(dip, XFS_ATTR_FORK, &nextents))
+ return true;
+
if (XFS_DFORK_BOFF(dip) == 0)
return dip->di_aformat != XFS_DINODE_FMT_EXTENTS ||
- dip->di_anextents != 0;
+ nextents != 0;

size = XFS_DFORK_SIZE(dip, sc->mp, XFS_ATTR_FORK);
switch (XFS_DFORK_FORMAT(dip, XFS_ATTR_FORK)) {
@@ -839,11 +848,15 @@ xrep_dinode_ensure_forkoff(
size_t bmdr_minsz = xfs_bmdr_space_calc(1);
unsigned int lit_sz = XFS_LITINO(sc->mp);
unsigned int afork_min, dfork_min;
+ int error;

trace_xrep_dinode_ensure_forkoff(sc, dip);

- dnextents = xfs_dfork_nextents(dip, XFS_DATA_FORK);
- anextents = xfs_dfork_nextents(dip, XFS_ATTR_FORK);
+ error = xfs_dfork_nextents(dip, XFS_DATA_FORK, &dnextents);
+ ASSERT(error == 0);
+
+ error = xfs_dfork_nextents(dip, XFS_ATTR_FORK, &anextents);
+ ASSERT(error == 0);

/*
* Before calling this function, xrep_dinode_core ensured that both
@@ -1031,6 +1044,7 @@ xrep_dinode_zap_forks(
uint16_t mode;
bool zap_datafork = false;
bool zap_attrfork = false;
+ int error;

trace_xrep_dinode_zap_forks(sc, dip);

@@ -1039,12 +1053,12 @@ xrep_dinode_zap_forks(
/* Inode counters don't make sense? */
nblocks = be64_to_cpu(dip->di_nblocks);

- nextents = xfs_dfork_nextents(dip, XFS_DATA_FORK);
- if (nextents > nblocks)
+ error = xfs_dfork_nextents(dip, XFS_DATA_FORK, &nextents);
+ if (error || nextents > nblocks)
zap_datafork = true;

- naextents = xfs_dfork_nextents(dip, XFS_ATTR_FORK);
- if (naextents > nblocks)
+ error = xfs_dfork_nextents(dip, XFS_ATTR_FORK, &naextents);
+ if (error || naextents > nblocks)
zap_attrfork = true;

if (nextents + naextents > nblocks)
--
2.30.2