diff options
Diffstat (limited to 'fs/xfs/scrub')
-rw-r--r-- | fs/xfs/scrub/agheader.c | 6 | ||||
-rw-r--r-- | fs/xfs/scrub/attr.c | 2 | ||||
-rw-r--r-- | fs/xfs/scrub/bmap.c | 174 | ||||
-rw-r--r-- | fs/xfs/scrub/common.c | 24 | ||||
-rw-r--r-- | fs/xfs/scrub/common.h | 13 | ||||
-rw-r--r-- | fs/xfs/scrub/dir.c | 2 | ||||
-rw-r--r-- | fs/xfs/scrub/ialloc.c | 5 | ||||
-rw-r--r-- | fs/xfs/scrub/inode.c | 298 | ||||
-rw-r--r-- | fs/xfs/scrub/parent.c | 12 | ||||
-rw-r--r-- | fs/xfs/scrub/quota.c | 2 | ||||
-rw-r--r-- | fs/xfs/scrub/rtbitmap.c | 3 | ||||
-rw-r--r-- | fs/xfs/scrub/trace.h | 31 |
12 files changed, 281 insertions, 291 deletions
diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c index 05c66e05ae20..018aabbd9394 100644 --- a/fs/xfs/scrub/agheader.c +++ b/fs/xfs/scrub/agheader.c @@ -80,7 +80,7 @@ xfs_scrub_walk_agfl( } /* first to the end */ - for (i = flfirst; i < XFS_AGFL_SIZE(mp); i++) { + for (i = flfirst; i < xfs_agfl_size(mp); i++) { error = fn(sc, be32_to_cpu(agfl_bno[i]), priv); if (error) return error; @@ -664,7 +664,7 @@ xfs_scrub_agf( if (agfl_last > agfl_first) fl_count = agfl_last - agfl_first + 1; else - fl_count = XFS_AGFL_SIZE(mp) - agfl_first + agfl_last + 1; + fl_count = xfs_agfl_size(mp) - agfl_first + agfl_last + 1; if (agfl_count != 0 && fl_count != agfl_count) xfs_scrub_block_set_corrupt(sc, sc->sa.agf_bp); @@ -791,7 +791,7 @@ xfs_scrub_agfl( /* Allocate buffer to ensure uniqueness of AGFL entries. */ agf = XFS_BUF_TO_AGF(sc->sa.agf_bp); agflcount = be32_to_cpu(agf->agf_flcount); - if (agflcount > XFS_AGFL_SIZE(sc->mp)) { + if (agflcount > xfs_agfl_size(sc->mp)) { xfs_scrub_block_set_corrupt(sc, sc->sa.agf_bp); goto out; } diff --git a/fs/xfs/scrub/attr.c b/fs/xfs/scrub/attr.c index 4ed80474f545..127575f0abfb 100644 --- a/fs/xfs/scrub/attr.c +++ b/fs/xfs/scrub/attr.c @@ -98,7 +98,7 @@ xfs_scrub_xattr_listent( if (flags & XFS_ATTR_INCOMPLETE) { /* Incomplete attr key, just mark the inode for preening. */ - xfs_scrub_ino_set_preen(sx->sc, context->dp->i_ino, NULL); + xfs_scrub_ino_set_preen(sx->sc, context->dp->i_ino); return; } diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c index d00282130492..639d14b51e90 100644 --- a/fs/xfs/scrub/bmap.c +++ b/fs/xfs/scrub/bmap.c @@ -37,6 +37,7 @@ #include "xfs_bmap_util.h" #include "xfs_bmap_btree.h" #include "xfs_rmap.h" +#include "xfs_rmap_btree.h" #include "xfs_refcount.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" @@ -423,6 +424,169 @@ xfs_scrub_bmap_btree( return error; } +struct xfs_scrub_bmap_check_rmap_info { + struct xfs_scrub_context *sc; + int whichfork; + struct xfs_iext_cursor icur; +}; + +/* Can we find bmaps that fit this rmap? */ +STATIC int +xfs_scrub_bmap_check_rmap( + struct xfs_btree_cur *cur, + struct xfs_rmap_irec *rec, + void *priv) +{ + struct xfs_bmbt_irec irec; + struct xfs_scrub_bmap_check_rmap_info *sbcri = priv; + struct xfs_ifork *ifp; + struct xfs_scrub_context *sc = sbcri->sc; + bool have_map; + + /* Is this even the right fork? */ + if (rec->rm_owner != sc->ip->i_ino) + return 0; + if ((sbcri->whichfork == XFS_ATTR_FORK) ^ + !!(rec->rm_flags & XFS_RMAP_ATTR_FORK)) + return 0; + if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK) + return 0; + + /* Now look up the bmbt record. */ + ifp = XFS_IFORK_PTR(sc->ip, sbcri->whichfork); + if (!ifp) { + xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork, + rec->rm_offset); + goto out; + } + have_map = xfs_iext_lookup_extent(sc->ip, ifp, rec->rm_offset, + &sbcri->icur, &irec); + if (!have_map) + xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork, + rec->rm_offset); + /* + * bmap extent record lengths are constrained to 2^21 blocks in length + * because of space constraints in the on-disk metadata structure. + * However, rmap extent record lengths are constrained only by AG + * length, so we have to loop through the bmbt to make sure that the + * entire rmap is covered by bmbt records. + */ + while (have_map) { + if (irec.br_startoff != rec->rm_offset) + xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork, + rec->rm_offset); + if (irec.br_startblock != XFS_AGB_TO_FSB(sc->mp, + cur->bc_private.a.agno, rec->rm_startblock)) + xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork, + rec->rm_offset); + if (irec.br_blockcount > rec->rm_blockcount) + xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork, + rec->rm_offset); + if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) + break; + rec->rm_startblock += irec.br_blockcount; + rec->rm_offset += irec.br_blockcount; + rec->rm_blockcount -= irec.br_blockcount; + if (rec->rm_blockcount == 0) + break; + have_map = xfs_iext_next_extent(ifp, &sbcri->icur, &irec); + if (!have_map) + xfs_scrub_fblock_set_corrupt(sc, sbcri->whichfork, + rec->rm_offset); + } + +out: + if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) + return XFS_BTREE_QUERY_RANGE_ABORT; + return 0; +} + +/* Make sure each rmap has a corresponding bmbt entry. */ +STATIC int +xfs_scrub_bmap_check_ag_rmaps( + struct xfs_scrub_context *sc, + int whichfork, + xfs_agnumber_t agno) +{ + struct xfs_scrub_bmap_check_rmap_info sbcri; + struct xfs_btree_cur *cur; + struct xfs_buf *agf; + int error; + + error = xfs_alloc_read_agf(sc->mp, sc->tp, agno, 0, &agf); + if (error) + return error; + + cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf, agno); + if (!cur) { + error = -ENOMEM; + goto out_agf; + } + + sbcri.sc = sc; + sbcri.whichfork = whichfork; + error = xfs_rmap_query_all(cur, xfs_scrub_bmap_check_rmap, &sbcri); + if (error == XFS_BTREE_QUERY_RANGE_ABORT) + error = 0; + + xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR); +out_agf: + xfs_trans_brelse(sc->tp, agf); + return error; +} + +/* Make sure each rmap has a corresponding bmbt entry. */ +STATIC int +xfs_scrub_bmap_check_rmaps( + struct xfs_scrub_context *sc, + int whichfork) +{ + loff_t size; + xfs_agnumber_t agno; + int error; + + if (!xfs_sb_version_hasrmapbt(&sc->mp->m_sb) || + whichfork == XFS_COW_FORK || + (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) + return 0; + + /* Don't support realtime rmap checks yet. */ + if (XFS_IS_REALTIME_INODE(sc->ip) && whichfork == XFS_DATA_FORK) + return 0; + + /* + * Only do this for complex maps that are in btree format, or for + * situations where we would seem to have a size but zero extents. + * The inode repair code can zap broken iforks, which means we have + * to flag this bmap as corrupt if there are rmaps that need to be + * reattached. + */ + switch (whichfork) { + case XFS_DATA_FORK: + size = i_size_read(VFS_I(sc->ip)); + break; + case XFS_ATTR_FORK: + size = XFS_IFORK_Q(sc->ip); + break; + default: + size = 0; + break; + } + if (XFS_IFORK_FORMAT(sc->ip, whichfork) != XFS_DINODE_FMT_BTREE && + (size == 0 || XFS_IFORK_NEXTENTS(sc->ip, whichfork) > 0)) + return 0; + + for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) { + error = xfs_scrub_bmap_check_ag_rmaps(sc, whichfork, agno); + if (error) + return error; + if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) + break; + } + + return 0; +} + /* * Scrub an inode fork's block mappings. * @@ -457,16 +621,16 @@ xfs_scrub_bmap( goto out; /* No CoW forks on non-reflink inodes/filesystems. */ if (!xfs_is_reflink_inode(ip)) { - xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL); + xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino); goto out; } break; case XFS_ATTR_FORK: if (!ifp) - goto out; + goto out_check_rmap; if (!xfs_sb_version_hasattr(&mp->m_sb) && !xfs_sb_version_hasattr2(&mp->m_sb)) - xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL); + xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino); break; default: ASSERT(whichfork == XFS_DATA_FORK); @@ -534,6 +698,10 @@ xfs_scrub_bmap( goto out; } +out_check_rmap: + error = xfs_scrub_bmap_check_rmaps(sc, whichfork); + if (!xfs_scrub_fblock_xref_process_error(sc, whichfork, 0, &error)) + goto out; out: return error; } diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c index 8033ab9d8f47..8ed91d5c868d 100644 --- a/fs/xfs/scrub/common.c +++ b/fs/xfs/scrub/common.c @@ -213,12 +213,10 @@ xfs_scrub_block_set_preen( void xfs_scrub_ino_set_preen( struct xfs_scrub_context *sc, - xfs_ino_t ino, - struct xfs_buf *bp) + xfs_ino_t ino) { sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN; - trace_xfs_scrub_ino_preen(sc, ino, bp ? bp->b_bn : 0, - __return_address); + trace_xfs_scrub_ino_preen(sc, ino, __return_address); } /* Record a corrupt block. */ @@ -249,22 +247,20 @@ xfs_scrub_block_xref_set_corrupt( void xfs_scrub_ino_set_corrupt( struct xfs_scrub_context *sc, - xfs_ino_t ino, - struct xfs_buf *bp) + xfs_ino_t ino) { sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT; - trace_xfs_scrub_ino_error(sc, ino, bp ? bp->b_bn : 0, __return_address); + trace_xfs_scrub_ino_error(sc, ino, __return_address); } /* Record a corruption while cross-referencing with an inode. */ void xfs_scrub_ino_xref_set_corrupt( struct xfs_scrub_context *sc, - xfs_ino_t ino, - struct xfs_buf *bp) + xfs_ino_t ino) { sc->sm->sm_flags |= XFS_SCRUB_OFLAG_XCORRUPT; - trace_xfs_scrub_ino_error(sc, ino, bp ? bp->b_bn : 0, __return_address); + trace_xfs_scrub_ino_error(sc, ino, __return_address); } /* Record corruption in a block indexed by a file fork. */ @@ -296,12 +292,10 @@ xfs_scrub_fblock_xref_set_corrupt( void xfs_scrub_ino_set_warning( struct xfs_scrub_context *sc, - xfs_ino_t ino, - struct xfs_buf *bp) + xfs_ino_t ino) { sc->sm->sm_flags |= XFS_SCRUB_OFLAG_WARNING; - trace_xfs_scrub_ino_warning(sc, ino, bp ? bp->b_bn : 0, - __return_address); + trace_xfs_scrub_ino_warning(sc, ino, __return_address); } /* Warn about a block indexed by a file fork that needs review. */ @@ -619,7 +613,7 @@ xfs_scrub_checkpoint_log( { int error; - error = _xfs_log_force(mp, XFS_LOG_SYNC, NULL); + error = xfs_log_force(mp, XFS_LOG_SYNC); if (error) return error; xfs_ail_push_all_sync(mp->m_ail); diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h index ddb65d22c76a..deaf60400981 100644 --- a/fs/xfs/scrub/common.h +++ b/fs/xfs/scrub/common.h @@ -63,25 +63,22 @@ bool xfs_scrub_fblock_xref_process_error(struct xfs_scrub_context *sc, void xfs_scrub_block_set_preen(struct xfs_scrub_context *sc, struct xfs_buf *bp); -void xfs_scrub_ino_set_preen(struct xfs_scrub_context *sc, xfs_ino_t ino, - struct xfs_buf *bp); +void xfs_scrub_ino_set_preen(struct xfs_scrub_context *sc, xfs_ino_t ino); void xfs_scrub_block_set_corrupt(struct xfs_scrub_context *sc, struct xfs_buf *bp); -void xfs_scrub_ino_set_corrupt(struct xfs_scrub_context *sc, xfs_ino_t ino, - struct xfs_buf *bp); +void xfs_scrub_ino_set_corrupt(struct xfs_scrub_context *sc, xfs_ino_t ino); void xfs_scrub_fblock_set_corrupt(struct xfs_scrub_context *sc, int whichfork, xfs_fileoff_t offset); void xfs_scrub_block_xref_set_corrupt(struct xfs_scrub_context *sc, struct xfs_buf *bp); -void xfs_scrub_ino_xref_set_corrupt(struct xfs_scrub_context *sc, xfs_ino_t ino, - struct xfs_buf *bp); +void xfs_scrub_ino_xref_set_corrupt(struct xfs_scrub_context *sc, + xfs_ino_t ino); void xfs_scrub_fblock_xref_set_corrupt(struct xfs_scrub_context *sc, int whichfork, xfs_fileoff_t offset); -void xfs_scrub_ino_set_warning(struct xfs_scrub_context *sc, xfs_ino_t ino, - struct xfs_buf *bp); +void xfs_scrub_ino_set_warning(struct xfs_scrub_context *sc, xfs_ino_t ino); void xfs_scrub_fblock_set_warning(struct xfs_scrub_context *sc, int whichfork, xfs_fileoff_t offset); diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c index 50b6a26b0299..38f29806eb54 100644 --- a/fs/xfs/scrub/dir.c +++ b/fs/xfs/scrub/dir.c @@ -781,7 +781,7 @@ xfs_scrub_directory( /* Plausible size? */ if (sc->ip->i_d.di_size < xfs_dir2_sf_hdr_size(0)) { - xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL); + xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino); goto out; } diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c index 63ab3f98430d..106ca4bd753f 100644 --- a/fs/xfs/scrub/ialloc.c +++ b/fs/xfs/scrub/ialloc.c @@ -259,7 +259,8 @@ xfs_scrub_iallocbt_check_freemask( error = xfs_imap_to_bp(mp, bs->cur->bc_tp, &imap, &dip, &bp, 0, 0); - if (!xfs_scrub_btree_process_error(bs->sc, bs->cur, 0, &error)) + if (!xfs_scrub_btree_xref_process_error(bs->sc, bs->cur, 0, + &error)) continue; /* Which inodes are free? */ @@ -433,7 +434,7 @@ xfs_scrub_iallocbt_xref_rmap_inodes( if (!xfs_scrub_should_check_xref(sc, &error, &sc->sa.rmap_cur)) return; if (blocks != inode_blocks) - xfs_scrub_btree_set_corrupt(sc, sc->sa.ino_cur, 0); + xfs_scrub_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0); } /* Scrub the inode btrees for some AG. */ diff --git a/fs/xfs/scrub/inode.c b/fs/xfs/scrub/inode.c index 21297bef8df1..df14930e4fc5 100644 --- a/fs/xfs/scrub/inode.c +++ b/fs/xfs/scrub/inode.c @@ -89,67 +89,21 @@ out: /* Inode core */ -/* - * Validate di_extsize hint. - * - * The rules are documented at xfs_ioctl_setattr_check_extsize(). - * These functions must be kept in sync with each other. - */ +/* Validate di_extsize hint. */ STATIC void xfs_scrub_inode_extsize( struct xfs_scrub_context *sc, - struct xfs_buf *bp, struct xfs_dinode *dip, xfs_ino_t ino, uint16_t mode, uint16_t flags) { - struct xfs_mount *mp = sc->mp; - bool rt_flag; - bool hint_flag; - bool inherit_flag; - uint32_t extsize; - uint32_t extsize_bytes; - uint32_t blocksize_bytes; - - rt_flag = (flags & XFS_DIFLAG_REALTIME); - hint_flag = (flags & XFS_DIFLAG_EXTSIZE); - inherit_flag = (flags & XFS_DIFLAG_EXTSZINHERIT); - extsize = be32_to_cpu(dip->di_extsize); - extsize_bytes = XFS_FSB_TO_B(sc->mp, extsize); - - if (rt_flag) - blocksize_bytes = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog; - else - blocksize_bytes = mp->m_sb.sb_blocksize; - - if ((hint_flag || inherit_flag) && !(S_ISDIR(mode) || S_ISREG(mode))) - goto bad; - - if (hint_flag && !S_ISREG(mode)) - goto bad; - - if (inherit_flag && !S_ISDIR(mode)) - goto bad; - - if ((hint_flag || inherit_flag) && extsize == 0) - goto bad; - - if (!(hint_flag || inherit_flag) && extsize != 0) - goto bad; - - if (extsize_bytes % blocksize_bytes) - goto bad; - - if (extsize > MAXEXTLEN) - goto bad; + xfs_failaddr_t fa; - if (!rt_flag && extsize > mp->m_sb.sb_agblocks / 2) - goto bad; - - return; -bad: - xfs_scrub_ino_set_corrupt(sc, ino, bp); + fa = xfs_inode_validate_extsize(sc->mp, be32_to_cpu(dip->di_extsize), + mode, flags); + if (fa) + xfs_scrub_ino_set_corrupt(sc, ino); } /* @@ -161,58 +115,25 @@ bad: STATIC void xfs_scrub_inode_cowextsize( struct xfs_scrub_context *sc, - struct xfs_buf *bp, struct xfs_dinode *dip, xfs_ino_t ino, uint16_t mode, uint16_t flags, uint64_t flags2) { - struct xfs_mount *mp = sc->mp; - bool rt_flag; - bool hint_flag; - uint32_t extsize; - uint32_t extsize_bytes; - - rt_flag = (flags & XFS_DIFLAG_REALTIME); - hint_flag = (flags2 & XFS_DIFLAG2_COWEXTSIZE); - extsize = be32_to_cpu(dip->di_cowextsize); - extsize_bytes = XFS_FSB_TO_B(sc->mp, extsize); - - if (hint_flag && !xfs_sb_version_hasreflink(&mp->m_sb)) - goto bad; - - if (hint_flag && !(S_ISDIR(mode) || S_ISREG(mode))) - goto bad; - - if (hint_flag && extsize == 0) - goto bad; - - if (!hint_flag && extsize != 0) - goto bad; - - if (hint_flag && rt_flag) - goto bad; - - if (extsize_bytes % mp->m_sb.sb_blocksize) - goto bad; - - if (extsize > MAXEXTLEN) - goto bad; - - if (extsize > mp->m_sb.sb_agblocks / 2) - goto bad; + xfs_failaddr_t fa; - return; -bad: - xfs_scrub_ino_set_corrupt(sc, ino, bp); + fa = xfs_inode_validate_cowextsize(sc->mp, + be32_to_cpu(dip->di_cowextsize), mode, flags, + flags2); + if (fa) + xfs_scrub_ino_set_corrupt(sc, ino); } /* Make sure the di_flags make sense for the inode. */ STATIC void xfs_scrub_inode_flags( struct xfs_scrub_context *sc, - struct xfs_buf *bp, struct xfs_dinode *dip, xfs_ino_t ino, uint16_t mode, @@ -251,14 +172,13 @@ xfs_scrub_inode_flags( return; bad: - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); } /* Make sure the di_flags2 make sense for the inode. */ STATIC void xfs_scrub_inode_flags2( struct xfs_scrub_context *sc, - struct xfs_buf *bp, struct xfs_dinode *dip, xfs_ino_t ino, uint16_t mode, @@ -295,14 +215,13 @@ xfs_scrub_inode_flags2( return; bad: - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); } /* Scrub all the ondisk inode fields. */ STATIC void xfs_scrub_dinode( struct xfs_scrub_context *sc, - struct xfs_buf *bp, struct xfs_dinode *dip, xfs_ino_t ino) { @@ -333,7 +252,7 @@ xfs_scrub_dinode( /* mode is recognized */ break; default: - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); break; } @@ -344,22 +263,22 @@ xfs_scrub_dinode( * We autoconvert v1 inodes into v2 inodes on writeout, * so just mark this inode for preening. */ - xfs_scrub_ino_set_preen(sc, ino, bp); + xfs_scrub_ino_set_preen(sc, ino); break; case 2: case 3: if (dip->di_onlink != 0) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); if (dip->di_mode == 0 && sc->ip) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); if (dip->di_projid_hi != 0 && !xfs_sb_version_hasprojid32bit(&mp->m_sb)) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); break; default: - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); return; } @@ -369,40 +288,40 @@ xfs_scrub_dinode( */ if (dip->di_uid == cpu_to_be32(-1U) || dip->di_gid == cpu_to_be32(-1U)) - xfs_scrub_ino_set_warning(sc, ino, bp); + xfs_scrub_ino_set_warning(sc, ino); /* di_format */ switch (dip->di_format) { case XFS_DINODE_FMT_DEV: if (!S_ISCHR(mode) && !S_ISBLK(mode) && !S_ISFIFO(mode) && !S_ISSOCK(mode)) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); break; case XFS_DINODE_FMT_LOCAL: if (!S_ISDIR(mode) && !S_ISLNK(mode)) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); break; case XFS_DINODE_FMT_EXTENTS: if (!S_ISREG(mode) && !S_ISDIR(mode) && !S_ISLNK(mode)) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); break; case XFS_DINODE_FMT_BTREE: if (!S_ISREG(mode) && !S_ISDIR(mode)) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); break; case XFS_DINODE_FMT_UUID: default: - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); break; } /* di_[amc]time.nsec */ if (be32_to_cpu(dip->di_atime.t_nsec) >= NSEC_PER_SEC) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); if (be32_to_cpu(dip->di_mtime.t_nsec) >= NSEC_PER_SEC) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); if (be32_to_cpu(dip->di_ctime.t_nsec) >= NSEC_PER_SEC) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); /* * di_size. xfs_dinode_verify checks for things that screw up @@ -411,19 +330,19 @@ xfs_scrub_dinode( */ isize = be64_to_cpu(dip->di_size); if (isize & (1ULL << 63)) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); /* Devices, fifos, and sockets must have zero size */ if (!S_ISDIR(mode) && !S_ISREG(mode) && !S_ISLNK(mode) && isize != 0) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); /* Directories can't be larger than the data section size (32G) */ if (S_ISDIR(mode) && (isize == 0 || isize >= XFS_DIR2_SPACE_SIZE)) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); /* Symlinks can't be larger than SYMLINK_MAXLEN */ if (S_ISLNK(mode) && (isize == 0 || isize >= XFS_SYMLINK_MAXLEN)) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); /* * Warn if the running kernel can't handle the kinds of offsets @@ -432,7 +351,7 @@ xfs_scrub_dinode( * overly large offsets, flag the inode for admin review. */ if (isize >= mp->m_super->s_maxbytes) - xfs_scrub_ino_set_warning(sc, ino, bp); + xfs_scrub_ino_set_warning(sc, ino); /* di_nblocks */ if (flags2 & XFS_DIFLAG2_REFLINK) { @@ -447,15 +366,15 @@ xfs_scrub_dinode( */ if (be64_to_cpu(dip->di_nblocks) >= mp->m_sb.sb_dblocks + mp->m_sb.sb_rblocks) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); } else { if (be64_to_cpu(dip->di_nblocks) >= mp->m_sb.sb_dblocks) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); } - xfs_scrub_inode_flags(sc, bp, dip, ino, mode, flags); + xfs_scrub_inode_flags(sc, dip, ino, mode, flags); - xfs_scrub_inode_extsize(sc, bp, dip, ino, mode, flags); + xfs_scrub_inode_extsize(sc, dip, ino, mode, flags); /* di_nextents */ nextents = be32_to_cpu(dip->di_nextents); @@ -463,31 +382,31 @@ xfs_scrub_dinode( switch (dip->di_format) { case XFS_DINODE_FMT_EXTENTS: if (nextents > fork_recs) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); break; case XFS_DINODE_FMT_BTREE: if (nextents <= fork_recs) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); break; default: if (nextents != 0) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); break; } /* di_forkoff */ if (XFS_DFORK_APTR(dip) >= (char *)dip + mp->m_sb.sb_inodesize) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); if (dip->di_anextents != 0 && dip->di_forkoff == 0) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); if (dip->di_forkoff == 0 && dip->di_aformat != XFS_DINODE_FMT_EXTENTS) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); /* di_aformat */ if (dip->di_aformat != XFS_DINODE_FMT_LOCAL && dip->di_aformat != XFS_DINODE_FMT_EXTENTS && dip->di_aformat != XFS_DINODE_FMT_BTREE) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); /* di_anextents */ nextents = be16_to_cpu(dip->di_anextents); @@ -495,92 +414,26 @@ xfs_scrub_dinode( switch (dip->di_aformat) { case XFS_DINODE_FMT_EXTENTS: if (nextents > fork_recs) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); break; case XFS_DINODE_FMT_BTREE: if (nextents <= fork_recs) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); break; default: if (nextents != 0) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); } if (dip->di_version >= 3) { if (be32_to_cpu(dip->di_crtime.t_nsec) >= NSEC_PER_SEC) - xfs_scrub_ino_set_corrupt(sc, ino, bp); - xfs_scrub_inode_flags2(sc, bp, dip, ino, mode, flags, flags2); - xfs_scrub_inode_cowextsize(sc, bp, dip, ino, mode, flags, + xfs_scrub_ino_set_corrupt(sc, ino); + xfs_scrub_inode_flags2(sc, dip, ino, mode, flags, flags2); + xfs_scrub_inode_cowextsize(sc, dip, ino, mode, flags, flags2); } } -/* Map and read a raw inode. */ -STATIC int -xfs_scrub_inode_map_raw( - struct xfs_scrub_context *sc, - xfs_ino_t ino, - struct xfs_buf **bpp, - struct xfs_dinode **dipp) -{ - struct xfs_imap imap; - struct xfs_mount *mp = sc->mp; - struct xfs_buf *bp = NULL; - struct xfs_dinode *dip; - int error; - - error = xfs_imap(mp, sc->tp, ino, &imap, XFS_IGET_UNTRUSTED); - if (error == -EINVAL) { - /* - * Inode could have gotten deleted out from under us; - * just forget about it. - */ - error = -ENOENT; - goto out; - } - if (!xfs_scrub_process_error(sc, XFS_INO_TO_AGNO(mp, ino), - XFS_INO_TO_AGBNO(mp, ino), &error)) - goto out; - - error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp, - imap.im_blkno, imap.im_len, XBF_UNMAPPED, &bp, - NULL); - if (!xfs_scrub_process_error(sc, XFS_INO_TO_AGNO(mp, ino), - XFS_INO_TO_AGBNO(mp, ino), &error)) - goto out; - - /* - * Is this really an inode? We disabled verifiers in the above - * xfs_trans_read_buf call because the inode buffer verifier - * fails on /any/ inode record in the inode cluster with a bad - * magic or version number, not just the one that we're - * checking. Therefore, grab the buffer unconditionally, attach - * the inode verifiers by hand, and run the inode verifier only - * on the one inode we want. - */ - bp->b_ops = &xfs_inode_buf_ops; - dip = xfs_buf_offset(bp, imap.im_boffset); - if (xfs_dinode_verify(mp, ino, dip) != NULL || - !xfs_dinode_good_version(mp, dip->di_version)) { - xfs_scrub_ino_set_corrupt(sc, ino, bp); - goto out_buf; - } - - /* ...and is it the one we asked for? */ - if (be32_to_cpu(dip->di_gen) != sc->sm->sm_gen) { - error = -ENOENT; - goto out_buf; - } - - *dipp = dip; - *bpp = bp; -out: - return error; -out_buf: - xfs_trans_brelse(sc->tp, bp); - return error; -} - /* * Make sure the finobt doesn't think this inode is free. * We don't have to check the inobt ourselves because we got the inode via @@ -645,18 +498,18 @@ xfs_scrub_inode_xref_bmap( if (!xfs_scrub_should_check_xref(sc, &error, NULL)) return; if (nextents < be32_to_cpu(dip->di_nextents)) - xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino, NULL); + xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino); error = xfs_bmap_count_blocks(sc->tp, sc->ip, XFS_ATTR_FORK, &nextents, &acount); if (!xfs_scrub_should_check_xref(sc, &error, NULL)) return; if (nextents != be16_to_cpu(dip->di_anextents)) - xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino, NULL); + xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino); /* Check nblocks against the inode. */ if (count + acount != be64_to_cpu(dip->di_nblocks)) - xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino, NULL); + xfs_scrub_ino_xref_set_corrupt(sc, sc->ip->i_ino); } /* Cross-reference with the other btrees. */ @@ -700,8 +553,7 @@ xfs_scrub_inode_xref( static void xfs_scrub_inode_check_reflink_iflag( struct xfs_scrub_context *sc, - xfs_ino_t ino, - struct xfs_buf *bp) + xfs_ino_t ino) { struct xfs_mount *mp = sc->mp; bool has_shared; @@ -716,9 +568,9 @@ xfs_scrub_inode_check_reflink_iflag( XFS_INO_TO_AGBNO(mp, ino), &error)) return; if (xfs_is_reflink_inode(sc->ip) && !has_shared) - xfs_scrub_ino_set_preen(sc, ino, bp); + xfs_scrub_ino_set_preen(sc, ino); else if (!xfs_is_reflink_inode(sc->ip) && has_shared) - xfs_scrub_ino_set_corrupt(sc, ino, bp); + xfs_scrub_ino_set_corrupt(sc, ino); } /* Scrub an inode. */ @@ -727,43 +579,33 @@ xfs_scrub_inode( struct xfs_scrub_context *sc) { struct xfs_dinode di; - struct xfs_buf *bp = NULL; - struct xfs_dinode *dip; - xfs_ino_t ino; int error = 0; - /* Did we get the in-core inode, or are we doing this manually? */ - if (sc->ip) { - ino = sc->ip->i_ino; - xfs_inode_to_disk(sc->ip, &di, 0); - dip = &di; - } else { - /* Map & read inode. */ - ino = sc->sm->sm_ino; - error = xfs_scrub_inode_map_raw(sc, ino, &bp, &dip); - if (error || !bp) - goto out; + /* + * If sc->ip is NULL, that means that the setup function called + * xfs_iget to look up the inode. xfs_iget returned a EFSCORRUPTED + * and a NULL inode, so flag the corruption error and return. + */ + if (!sc->ip) { + xfs_scrub_ino_set_corrupt(sc, sc->sm->sm_ino); + return 0; } - xfs_scrub_dinode(sc, bp, dip, ino); + /* Scrub the inode core. */ + xfs_inode_to_disk(sc->ip, &di, 0); + xfs_scrub_dinode(sc, &di, sc->ip->i_ino); if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) goto out; - /* Now let's do the things that require a live inode. */ - if (!sc->ip) - goto out; - /* * Look for discrepancies between file's data blocks and the reflink * iflag. We already checked the iflag against the file mode when * we scrubbed the dinode. */ if (S_ISREG(VFS_I(sc->ip)->i_mode)) - xfs_scrub_inode_check_reflink_iflag(sc, ino, bp); + xfs_scrub_inode_check_reflink_iflag(sc, sc->ip->i_ino); - xfs_scrub_inode_xref(sc, ino, dip); + xfs_scrub_inode_xref(sc, sc->ip->i_ino, &di); out: - if (bp) - xfs_trans_brelse(sc->tp, bp); return error; } diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c index 0d3851410c74..1fb88c18d455 100644 --- a/fs/xfs/scrub/parent.c +++ b/fs/xfs/scrub/parent.c @@ -167,8 +167,18 @@ xfs_scrub_parent_validate( * if the parent pointer erroneously points to a file, we * can't use DONTCACHE here because DONTCACHE inodes can trigger * immediate inactive cleanup of the inode. + * + * If _iget returns -EINVAL then the parent inode number is garbage + * and the directory is corrupt. If the _iget returns -EFSCORRUPTED + * or -EFSBADCRC then the parent is corrupt which is a cross + * referencing error. Any other error is an operational error. */ - error = xfs_iget(mp, sc->tp, dnum, 0, 0, &dp); + error = xfs_iget(mp, sc->tp, dnum, XFS_IGET_UNTRUSTED, 0, &dp); + if (error == -EINVAL) { + error = -EFSCORRUPTED; + xfs_scrub_fblock_process_error(sc, XFS_DATA_FORK, 0, &error); + goto out; + } if (!xfs_scrub_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error)) goto out; if (dp == sc->ip || !S_ISDIR(VFS_I(dp)->i_mode)) { diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c index 51daa4ae2627..6ba465e6c885 100644 --- a/fs/xfs/scrub/quota.c +++ b/fs/xfs/scrub/quota.c @@ -219,7 +219,7 @@ xfs_scrub_quota( /* Look for problem extents. */ xfs_ilock(ip, XFS_ILOCK_EXCL); if (ip->i_d.di_flags & XFS_DIFLAG_REALTIME) { - xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino, NULL); + xfs_scrub_ino_set_corrupt(sc, sc->ip->i_ino); goto out_unlock_inode; } max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk; diff --git a/fs/xfs/scrub/rtbitmap.c b/fs/xfs/scrub/rtbitmap.c index 26390991369a..39c41dfe08ee 100644 --- a/fs/xfs/scrub/rtbitmap.c +++ b/fs/xfs/scrub/rtbitmap.c @@ -116,8 +116,7 @@ xfs_scrub_xref_is_used_rt_space( if (!xfs_scrub_should_check_xref(sc, &error, NULL)) goto out_unlock; if (is_free) - xfs_scrub_ino_xref_set_corrupt(sc, sc->mp->m_rbmip->i_ino, - NULL); + xfs_scrub_ino_xref_set_corrupt(sc, sc->mp->m_rbmip->i_ino); out_unlock: xfs_iunlock(sc->mp->m_rbmip, XFS_ILOCK_SHARED | XFS_ILOCK_RTBITMAP); } diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index 4dc896852bf0..5d2b1c241be5 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -174,53 +174,32 @@ DEFINE_SCRUB_BLOCK_ERROR_EVENT(xfs_scrub_block_error); DEFINE_SCRUB_BLOCK_ERROR_EVENT(xfs_scrub_block_preen); DECLARE_EVENT_CLASS(xfs_scrub_ino_error_class, - TP_PROTO(struct xfs_scrub_context *sc, xfs_ino_t ino, xfs_daddr_t daddr, - void *ret_ip), - TP_ARGS(sc, ino, daddr, ret_ip), + TP_PROTO(struct xfs_scrub_context *sc, xfs_ino_t ino, void *ret_ip), + TP_ARGS(sc, ino, ret_ip), TP_STRUCT__entry( __field(dev_t, dev) __field(xfs_ino_t, ino) __field(unsigned int, type) - __field(xfs_agnumber_t, agno) - __field(xfs_agblock_t, bno) __field(void *, ret_ip) ), TP_fast_assign( - xfs_fsblock_t fsbno; - xfs_agnumber_t agno; - xfs_agblock_t bno; - - if (daddr) { - fsbno = XFS_DADDR_TO_FSB(sc->mp, daddr); - agno = XFS_FSB_TO_AGNO(sc->mp, fsbno); - bno = XFS_FSB_TO_AGBNO(sc->mp, fsbno); - } else { - agno = XFS_INO_TO_AGNO(sc->mp, ino); - bno = XFS_AGINO_TO_AGBNO(sc->mp, - XFS_INO_TO_AGINO(sc->mp, ino)); - } - __entry->dev = sc->mp->m_super->s_dev; __entry->ino = ino; __entry->type = sc->sm->sm_type; - __entry->agno = agno; - __entry->bno = bno; __entry->ret_ip = ret_ip; ), - TP_printk("dev %d:%d ino 0x%llx type %u agno %u agbno %u ret_ip %pS", + TP_printk("dev %d:%d ino 0x%llx type %u ret_ip %pS", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, __entry->type, - __entry->agno, - __entry->bno, __entry->ret_ip) ) #define DEFINE_SCRUB_INO_ERROR_EVENT(name) \ DEFINE_EVENT(xfs_scrub_ino_error_class, name, \ TP_PROTO(struct xfs_scrub_context *sc, xfs_ino_t ino, \ - xfs_daddr_t daddr, void *ret_ip), \ - TP_ARGS(sc, ino, daddr, ret_ip)) + void *ret_ip), \ + TP_ARGS(sc, ino, ret_ip)) DEFINE_SCRUB_INO_ERROR_EVENT(xfs_scrub_ino_error); DEFINE_SCRUB_INO_ERROR_EVENT(xfs_scrub_ino_preen); |