enum reflink_scan_state {
RLS_IRRELEVANT = -1, /* not applicable to this file */
RLS_UNKNOWN, /* shared extent scans required */
RLS_SET_IFLAG, /* iflag must be set */
};
/* List of new bmap records. */ struct xfarray *bmap_records;
struct xfs_scrub *sc;
/* How many blocks did we find allocated to this file? */
xfs_rfsblock_t nblocks;
/* How many bmbt blocks did we find for this fork? */
xfs_rfsblock_t old_bmbt_block_count;
/* get_records()'s position in the free space record array. */
xfarray_idx_t array_cur;
/* How many real (non-hole, non-delalloc) mappings do we have? */
uint64_t real_mappings;
/* Which fork are we fixing? */ int whichfork;
/* What d the REFLINK flag be set when the repair is over? */ enum reflink_scan_state reflink_scan;
/* Do we allow unwritten extents? */ bool allow_unwritten;
};
/* Is this space extent shared? Flag the inode if it is. */ STATICint
xrep_bmap_discover_shared( struct xrep_bmap *rb,
xfs_fsblock_t startblock,
xfs_filblks_t blockcount)
{ struct xfs_scrub *sc = rb->sc; struct xfs_btree_cur *cur;
xfs_agblock_t agbno;
xfs_agblock_t fbno;
xfs_extlen_t flen; int error;
if (XFS_IS_REALTIME_INODE(sc->ip)) {
agbno = xfs_rtb_to_rgbno(sc->mp, startblock);
cur = sc->sr.refc_cur;
} else {
agbno = XFS_FSB_TO_AGBNO(sc->mp, startblock);
cur = sc->sa.refc_cur;
}
error = xfs_refcount_find_shared(cur, agbno, blockcount, &fbno, &flen, false); if (error) return error;
if (fbno != NULLAGBLOCK)
rb->reflink_scan = RLS_SET_IFLAG;
return0;
}
/* Remember this reverse-mapping as a series of bmap records. */ STATICint
xrep_bmap_from_rmap( struct xrep_bmap *rb,
xfs_fileoff_t startoff,
xfs_fsblock_t startblock,
xfs_filblks_t blockcount, bool unwritten)
{ struct xfs_bmbt_irec irec = {
.br_startoff = startoff,
.br_startblock = startblock,
.br_state = unwritten ? XFS_EXT_UNWRITTEN : XFS_EXT_NORM,
}; struct xfs_bmbt_rec rbe; struct xfs_scrub *sc = rb->sc; int error = 0;
/* Check that this is within the AG. */ if (!xfs_verify_agbext(to_perag(cur->bc_group), rec->rm_startblock,
rec->rm_blockcount)) return -EFSCORRUPTED;
/* Check the file offset range. */ if (!(rec->rm_flags & XFS_RMAP_BMBT_BLOCK) &&
!xfs_verify_fileext(sc->mp, rec->rm_offset, rec->rm_blockcount)) return -EFSCORRUPTED;
/* No contradictory flags. */ if ((rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK)) &&
(rec->rm_flags & XFS_RMAP_UNWRITTEN)) return -EFSCORRUPTED;
/* Make sure this isn't free space. */
error = xfs_alloc_has_records(sc->sa.bno_cur, rec->rm_startblock,
rec->rm_blockcount, &outcome); if (error) return error; if (outcome != XBTREE_RECPACKING_EMPTY) return -EFSCORRUPTED;
/* Must not be an inode chunk. */
error = xfs_ialloc_has_inodes_at_extent(sc->sa.ino_cur,
rec->rm_startblock, rec->rm_blockcount, &outcome); if (error) return error; if (outcome != XBTREE_RECPACKING_EMPTY) return -EFSCORRUPTED;
return0;
}
/* Record extents that belong to this inode's fork. */ STATICint
xrep_bmap_walk_rmap( struct xfs_btree_cur *cur, conststruct xfs_rmap_irec *rec, void *priv)
{ struct xrep_bmap *rb = priv;
xfs_fsblock_t fsbno; int error = 0;
if (xchk_should_terminate(rb->sc, &error)) return error;
if (rec->rm_owner != rb->sc->ip->i_ino) return0;
error = xrep_bmap_check_fork_rmap(rb, cur, rec); if (error) return error;
/* If this rmap isn't for the fork we want, we're done. */ if (rb->whichfork == XFS_DATA_FORK &&
(rec->rm_flags & XFS_RMAP_ATTR_FORK)) return0; if (rb->whichfork == XFS_ATTR_FORK &&
!(rec->rm_flags & XFS_RMAP_ATTR_FORK)) return0;
/* Reject unwritten extents if we don't allow those. */ if ((rec->rm_flags & XFS_RMAP_UNWRITTEN) && !rb->allow_unwritten) return -EFSCORRUPTED;
/* Scan one AG for reverse mappings that we can turn into extent maps. */ STATICint
xrep_bmap_scan_ag( struct xrep_bmap *rb, struct xfs_perag *pag)
{ struct xfs_scrub *sc = rb->sc; int error;
error = xrep_ag_init(sc, pag, &sc->sa); if (error) return error;
#ifdef CONFIG_XFS_RT /* Check for any obvious errors or conflicts in the file mapping. */ STATICint
xrep_bmap_check_rtfork_rmap( struct xfs_scrub *sc, struct xfs_btree_cur *cur, conststruct xfs_rmap_irec *rec)
{ /* xattr extents are never stored on realtime devices */ if (rec->rm_flags & XFS_RMAP_ATTR_FORK) return -EFSCORRUPTED;
/* bmbt blocks are never stored on realtime devices */ if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK) return -EFSCORRUPTED;
/* Data extents for non-rt files are never stored on the rt device. */ if (!XFS_IS_REALTIME_INODE(sc->ip)) return -EFSCORRUPTED;
/* Check the file offsets and physical extents. */ if (!xfs_verify_fileext(sc->mp, rec->rm_offset, rec->rm_blockcount)) return -EFSCORRUPTED;
/* Check that this is within the rtgroup. */ if (!xfs_verify_rgbext(to_rtg(cur->bc_group), rec->rm_startblock,
rec->rm_blockcount)) return -EFSCORRUPTED;
/* Make sure this isn't free space. */ return xrep_require_rtext_inuse(sc, rec->rm_startblock,
rec->rm_blockcount);
}
/* Record realtime extents that belong to this inode's fork. */ STATICint
xrep_bmap_walk_rtrmap( struct xfs_btree_cur *cur, conststruct xfs_rmap_irec *rec, void *priv)
{ struct xrep_bmap *rb = priv; int error = 0;
if (xchk_should_terminate(rb->sc, &error)) return error;
/* Skip extents which are not owned by this inode and fork. */ if (rec->rm_owner != rb->sc->ip->i_ino) return0;
error = xrep_bmap_check_rtfork_rmap(rb->sc, cur, rec); if (error) return error;
/* If this rmap isn't for the fork we want, we're done. */ if (rb->whichfork == XFS_DATA_FORK &&
(rec->rm_flags & XFS_RMAP_ATTR_FORK)) return0; if (rb->whichfork == XFS_ATTR_FORK &&
!(rec->rm_flags & XFS_RMAP_ATTR_FORK)) return0;
/* Feed one of the new btree blocks to the bulk loader. */ STATICint
xrep_bmap_claim_block( struct xfs_btree_cur *cur, union xfs_btree_ptr *ptr, void *priv)
{ struct xrep_bmap *rb = priv;
/* Figure out how much space we need to create the incore btree root block. */ STATIC size_t
xrep_bmap_iroot_size( struct xfs_btree_cur *cur, unsignedint level, unsignedint nr_this_level, void *priv)
{
ASSERT(level > 0);
/* Reserve the space we'll need for the new btree. */
error = xrep_newbt_alloc_blocks(&rb->new_bmapbt,
rb->new_bmapbt.bload.nr_blocks); if (error) return error;
/* Add all observed bmap records. */
rb->array_cur = XFARRAY_CURSOR_INIT;
error = xfs_btree_bload(bmap_cur, &rb->new_bmapbt.bload, rb); if (error) return error;
/* Free the old bmbt blocks if they're not in use. */
xfs_rmap_ino_bmbt_owner(&oinfo, sc->ip->i_ino, rb->whichfork); return xrep_reap_fsblocks(sc, &rb->old_bmbt_blocks, &oinfo);
}
/* Check for garbage inputs. Returns -ECANCELED if there's nothing to do. */ STATICint
xrep_bmap_check_inputs( struct xfs_scrub *sc, int whichfork)
{ struct xfs_ifork *ifp = xfs_ifork_ptr(sc->ip, whichfork);
/* No fork means nothing to rebuild. */ if (!ifp) return -ECANCELED;
/* *Weonlyknowhowtorepairextentmappings,whichistosaythatwe *onlysupportextentsandbtreeforkformat.Repairstoalocal *formatforkrequireahigherlevelrepairfunction,sowedonot *haveanyworktodohere.
*/ switch (ifp->if_format) { case XFS_DINODE_FMT_DEV: case XFS_DINODE_FMT_LOCAL: case XFS_DINODE_FMT_UUID: case XFS_DINODE_FMT_META_BTREE: return -ECANCELED; case XFS_DINODE_FMT_EXTENTS: case XFS_DINODE_FMT_BTREE: break; default: return -EFSCORRUPTED;
}
if (whichfork == XFS_ATTR_FORK) return0;
/* Only files, symlinks, and directories get to have data forks. */ switch (VFS_I(sc->ip)->i_mode & S_IFMT) { case S_IFREG: case S_IFDIR: case S_IFLNK: /* ok */ break; default: return -EINVAL;
}
return0;
}
/* Set up the initial state of the reflink scan. */ staticinlineenum reflink_scan_state
xrep_bmap_init_reflink_scan( struct xfs_scrub *sc, int whichfork)
{ /* cannot share on non-reflink filesystem */ if (!xfs_has_reflink(sc->mp)) return RLS_IRRELEVANT;
/* preserve flag if it's already set */ if (xfs_is_reflink_inode(sc->ip)) return RLS_SET_IFLAG;
/* can only share regular files */ if (!S_ISREG(VFS_I(sc->ip)->i_mode)) return RLS_IRRELEVANT;
/* Set up enough storage to handle the max records for this fork. */
large_extcount = xfs_has_large_extent_counts(sc->mp);
max_bmbt_recs = xfs_iext_max_nextents(large_extcount, whichfork);
descr = xchk_xfile_ino_descr(sc, "%s fork mapping records",
whichfork == XFS_DATA_FORK ? "data" : "attr");
error = xfarray_create(descr, max_bmbt_recs, sizeof(struct xfs_bmbt_rec), &rb->bmap_records);
kfree(descr); if (error) goto out_rb;
/* Collect all reverse mappings for this fork's extents. */
xfsb_bitmap_init(&rb->old_bmbt_blocks);
error = xrep_bmap_find_mappings(rb); if (error) goto out_bitmap;
xfs_trans_ijoin(sc->tp, sc->ip, 0);
/* Rebuild the bmap information. */
error = xrep_bmap_build_new_fork(rb); if (error) goto out_bitmap;
/* Kill the old tree. */
error = xrep_bmap_remove_old_tree(rb); if (error) goto out_bitmap;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.