Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Darrick J. Wong | 2902 | 94.37% | 45 | 58.44% |
Christoph Hellwig | 92 | 2.99% | 14 | 18.18% |
David Chinner | 27 | 0.88% | 9 | 11.69% |
Chandan Babu R | 14 | 0.46% | 1 | 1.30% |
Russell Cattelan | 12 | 0.39% | 1 | 1.30% |
Eric Sandeen | 10 | 0.33% | 2 | 2.60% |
Michal Marek | 8 | 0.26% | 1 | 1.30% |
Lachlan McIlroy | 3 | 0.10% | 1 | 1.30% |
Mandy Kirkconnell | 3 | 0.10% | 1 | 1.30% |
Nathan Scott | 2 | 0.07% | 1 | 1.30% |
Jie Liu | 2 | 0.07% | 1 | 1.30% |
Total | 3075 | 77 |
// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <djwong@kernel.org> */ #include "xfs.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_trans_resv.h" #include "xfs_mount.h" #include "xfs_defer.h" #include "xfs_btree.h" #include "xfs_btree_staging.h" #include "xfs_bit.h" #include "xfs_log_format.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_inode.h" #include "xfs_inode_fork.h" #include "xfs_alloc.h" #include "xfs_rtalloc.h" #include "xfs_bmap.h" #include "xfs_bmap_util.h" #include "xfs_bmap_btree.h" #include "xfs_rmap.h" #include "xfs_rmap_btree.h" #include "xfs_refcount.h" #include "xfs_quota.h" #include "xfs_ialloc.h" #include "xfs_ag.h" #include "xfs_reflink.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/btree.h" #include "scrub/trace.h" #include "scrub/repair.h" #include "scrub/bitmap.h" #include "scrub/fsb_bitmap.h" #include "scrub/xfile.h" #include "scrub/xfarray.h" #include "scrub/newbt.h" #include "scrub/reap.h" /* * Inode Fork Block Mapping (BMBT) Repair * ====================================== * * Gather all the rmap records for the inode and fork we're fixing, reset the * incore fork, then recreate the btree. */ 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 */ }; struct xrep_bmap { /* Old bmbt blocks */ struct xfsb_bitmap old_bmbt_blocks; /* New fork. */ struct xrep_newbt new_bmapbt; /* 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. */ STATIC int xrep_bmap_discover_shared( struct xrep_bmap *rb, xfs_fsblock_t startblock, xfs_filblks_t blockcount) { struct xfs_scrub *sc = rb->sc; xfs_agblock_t agbno; xfs_agblock_t fbno; xfs_extlen_t flen; int error; agbno = XFS_FSB_TO_AGBNO(sc->mp, startblock); error = xfs_refcount_find_shared(sc->sa.refc_cur, agbno, blockcount, &fbno, &flen, false); if (error) return error; if (fbno != NULLAGBLOCK) rb->reflink_scan = RLS_SET_IFLAG; return 0; } /* Remember this reverse-mapping as a series of bmap records. */ STATIC int 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; /* * If we're repairing the data fork of a non-reflinked regular file on * a reflink filesystem, we need to figure out if this space extent is * shared. */ if (rb->reflink_scan == RLS_UNKNOWN && !unwritten) { error = xrep_bmap_discover_shared(rb, startblock, blockcount); if (error) return error; } do { xfs_failaddr_t fa; irec.br_blockcount = min_t(xfs_filblks_t, blockcount, XFS_MAX_BMBT_EXTLEN); fa = xfs_bmap_validate_extent(sc->ip, rb->whichfork, &irec); if (fa) return -EFSCORRUPTED; xfs_bmbt_disk_set_all(&rbe, &irec); trace_xrep_bmap_found(sc->ip, rb->whichfork, &irec); if (xchk_should_terminate(sc, &error)) return error; error = xfarray_append(rb->bmap_records, &rbe); if (error) return error; rb->real_mappings++; irec.br_startblock += irec.br_blockcount; irec.br_startoff += irec.br_blockcount; blockcount -= irec.br_blockcount; } while (blockcount > 0); return 0; } /* Check for any obvious errors or conflicts in the file mapping. */ STATIC int xrep_bmap_check_fork_rmap( struct xrep_bmap *rb, struct xfs_btree_cur *cur, const struct xfs_rmap_irec *rec) { struct xfs_scrub *sc = rb->sc; enum xbtree_recpacking outcome; int error; /* * Data extents for rt files are never stored on the data device, but * everything else (xattrs, bmbt blocks) can be. */ if (XFS_IS_REALTIME_INODE(sc->ip) && !(rec->rm_flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))) return -EFSCORRUPTED; /* Check that this is within the AG. */ if (!xfs_verify_agbext(cur->bc_ag.pag, 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; return 0; } /* Record extents that belong to this inode's fork. */ STATIC int xrep_bmap_walk_rmap( struct xfs_btree_cur *cur, const struct xfs_rmap_irec *rec, void *priv) { struct xrep_bmap *rb = priv; struct xfs_mount *mp = cur->bc_mp; 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) return 0; error = xrep_bmap_check_fork_rmap(rb, cur, rec); if (error) return error; /* * Record all blocks allocated to this file even if the extent isn't * for the fork we're rebuilding so that we can reset di_nblocks later. */ rb->nblocks += rec->rm_blockcount; /* 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)) return 0; if (rb->whichfork == XFS_ATTR_FORK && !(rec->rm_flags & XFS_RMAP_ATTR_FORK)) return 0; /* Reject unwritten extents if we don't allow those. */ if ((rec->rm_flags & XFS_RMAP_UNWRITTEN) && !rb->allow_unwritten) return -EFSCORRUPTED; fsbno = XFS_AGB_TO_FSB(mp, cur->bc_ag.pag->pag_agno, rec->rm_startblock); if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK) { rb->old_bmbt_block_count += rec->rm_blockcount; return xfsb_bitmap_set(&rb->old_bmbt_blocks, fsbno, rec->rm_blockcount); } return xrep_bmap_from_rmap(rb, rec->rm_offset, fsbno, rec->rm_blockcount, rec->rm_flags & XFS_RMAP_UNWRITTEN); } /* * Compare two block mapping records. We want to sort in order of increasing * file offset. */ static int xrep_bmap_extent_cmp( const void *a, const void *b) { const struct xfs_bmbt_rec *ba = a; const struct xfs_bmbt_rec *bb = b; xfs_fileoff_t ao = xfs_bmbt_disk_get_startoff(ba); xfs_fileoff_t bo = xfs_bmbt_disk_get_startoff(bb); if (ao > bo) return 1; else if (ao < bo) return -1; return 0; } /* * Sort the bmap extents by fork offset or else the records will be in the * wrong order. Ensure there are no overlaps in the file offset ranges. */ STATIC int xrep_bmap_sort_records( struct xrep_bmap *rb) { struct xfs_bmbt_irec irec; xfs_fileoff_t next_off = 0; xfarray_idx_t array_cur; int error; error = xfarray_sort(rb->bmap_records, xrep_bmap_extent_cmp, XFARRAY_SORT_KILLABLE); if (error) return error; foreach_xfarray_idx(rb->bmap_records, array_cur) { struct xfs_bmbt_rec rec; if (xchk_should_terminate(rb->sc, &error)) return error; error = xfarray_load(rb->bmap_records, array_cur, &rec); if (error) return error; xfs_bmbt_disk_get_all(&rec, &irec); if (irec.br_startoff < next_off) return -EFSCORRUPTED; next_off = irec.br_startoff + irec.br_blockcount; } return 0; } /* Scan one AG for reverse mappings that we can turn into extent maps. */ STATIC int 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; error = xfs_rmap_query_all(sc->sa.rmap_cur, xrep_bmap_walk_rmap, rb); xchk_ag_free(sc, &sc->sa); return error; } /* Find the delalloc extents from the old incore extent tree. */ STATIC int xrep_bmap_find_delalloc( struct xrep_bmap *rb) { struct xfs_bmbt_irec irec; struct xfs_iext_cursor icur; struct xfs_bmbt_rec rbe; struct xfs_inode *ip = rb->sc->ip; struct xfs_ifork *ifp = xfs_ifork_ptr(ip, rb->whichfork); int error = 0; /* * Skip this scan if we don't expect to find delayed allocation * reservations in this fork. */ if (rb->whichfork == XFS_ATTR_FORK || ip->i_delayed_blks == 0) return 0; for_each_xfs_iext(ifp, &icur, &irec) { if (!isnullstartblock(irec.br_startblock)) continue; xfs_bmbt_disk_set_all(&rbe, &irec); trace_xrep_bmap_found(ip, rb->whichfork, &irec); if (xchk_should_terminate(rb->sc, &error)) return error; error = xfarray_append(rb->bmap_records, &rbe); if (error) return error; } return 0; } /* * Collect block mappings for this fork of this inode and decide if we have * enough space to rebuild. Caller is responsible for cleaning up the list if * anything goes wrong. */ STATIC int xrep_bmap_find_mappings( struct xrep_bmap *rb) { struct xfs_scrub *sc = rb->sc; struct xfs_perag *pag; xfs_agnumber_t agno; int error = 0; /* Iterate the rmaps for extents. */ for_each_perag(sc->mp, agno, pag) { error = xrep_bmap_scan_ag(rb, pag); if (error) { xfs_perag_rele(pag); return error; } } return xrep_bmap_find_delalloc(rb); } /* Retrieve real extent mappings for bulk loading the bmap btree. */ STATIC int xrep_bmap_get_records( struct xfs_btree_cur *cur, unsigned int idx, struct xfs_btree_block *block, unsigned int nr_wanted, void *priv) { struct xfs_bmbt_rec rec; struct xfs_bmbt_irec *irec = &cur->bc_rec.b; struct xrep_bmap *rb = priv; union xfs_btree_rec *block_rec; unsigned int loaded; int error; for (loaded = 0; loaded < nr_wanted; loaded++, idx++) { do { error = xfarray_load(rb->bmap_records, rb->array_cur++, &rec); if (error) return error; xfs_bmbt_disk_get_all(&rec, irec); } while (isnullstartblock(irec->br_startblock)); block_rec = xfs_btree_rec_addr(cur, idx, block); cur->bc_ops->init_rec_from_cur(cur, block_rec); } return loaded; } /* Feed one of the new btree blocks to the bulk loader. */ STATIC int xrep_bmap_claim_block( struct xfs_btree_cur *cur, union xfs_btree_ptr *ptr, void *priv) { struct xrep_bmap *rb = priv; return xrep_newbt_claim_block(cur, &rb->new_bmapbt, ptr); } /* 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, unsigned int level, unsigned int nr_this_level, void *priv) { ASSERT(level > 0); return XFS_BMAP_BROOT_SPACE_CALC(cur->bc_mp, nr_this_level); } /* Update the inode counters. */ STATIC int xrep_bmap_reset_counters( struct xrep_bmap *rb) { struct xfs_scrub *sc = rb->sc; struct xbtree_ifakeroot *ifake = &rb->new_bmapbt.ifake; int64_t delta; if (rb->reflink_scan == RLS_SET_IFLAG) sc->ip->i_diflags2 |= XFS_DIFLAG2_REFLINK; /* * Update the inode block counts to reflect the extents we found in the * rmapbt. */ delta = ifake->if_blocks - rb->old_bmbt_block_count; sc->ip->i_nblocks = rb->nblocks + delta; xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE); /* * Adjust the quota counts by the difference in size between the old * and new bmbt. */ xfs_trans_mod_dquot_byino(sc->tp, sc->ip, XFS_TRANS_DQ_BCOUNT, delta); return 0; } /* * Create a new iext tree and load it with block mappings. If the inode is * in extents format, that's all we need to do to commit the new mappings. * If it is in btree format, this takes care of preloading the incore tree. */ STATIC int xrep_bmap_extents_load( struct xrep_bmap *rb) { struct xfs_iext_cursor icur; struct xfs_bmbt_irec irec; struct xfs_ifork *ifp = rb->new_bmapbt.ifake.if_fork; xfarray_idx_t array_cur; int error; ASSERT(ifp->if_bytes == 0); /* Add all the mappings (incl. delalloc) to the incore extent tree. */ xfs_iext_first(ifp, &icur); foreach_xfarray_idx(rb->bmap_records, array_cur) { struct xfs_bmbt_rec rec; error = xfarray_load(rb->bmap_records, array_cur, &rec); if (error) return error; xfs_bmbt_disk_get_all(&rec, &irec); xfs_iext_insert_raw(ifp, &icur, &irec); if (!isnullstartblock(irec.br_startblock)) ifp->if_nextents++; xfs_iext_next(ifp, &icur); } return xrep_ino_ensure_extent_count(rb->sc, rb->whichfork, ifp->if_nextents); } /* * Reserve new btree blocks, bulk load the bmap records into the ondisk btree, * and load the incore extent tree. */ STATIC int xrep_bmap_btree_load( struct xrep_bmap *rb, struct xfs_btree_cur *bmap_cur) { struct xfs_scrub *sc = rb->sc; int error; /* Compute how many blocks we'll need. */ error = xfs_btree_bload_compute_geometry(bmap_cur, &rb->new_bmapbt.bload, rb->real_mappings); if (error) return error; /* Last chance to abort before we start committing fixes. */ if (xchk_should_terminate(sc, &error)) return error; /* * Guess how many blocks we're going to need to rebuild an entire bmap * from the number of extents we found, and pump up our transaction to * have sufficient block reservation. We're allowed to exceed file * quota to repair inconsistent metadata. */ error = xfs_trans_reserve_more_inode(sc->tp, sc->ip, rb->new_bmapbt.bload.nr_blocks, 0, true); if (error) return error; /* 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; /* * Load the new bmap records into the new incore extent tree to * preserve delalloc reservations for regular files. The directory * code loads the extent tree during xfs_dir_open and assumes * thereafter that it remains loaded, so we must not violate that * assumption. */ return xrep_bmap_extents_load(rb); } /* * Use the collected bmap information to stage a new bmap fork. If this is * successful we'll return with the new fork information logged to the repair * transaction but not yet committed. The caller must ensure that the inode * is joined to the transaction; the inode will be joined to a clean * transaction when the function returns. */ STATIC int xrep_bmap_build_new_fork( struct xrep_bmap *rb) { struct xfs_owner_info oinfo; struct xfs_scrub *sc = rb->sc; struct xfs_btree_cur *bmap_cur; struct xbtree_ifakeroot *ifake = &rb->new_bmapbt.ifake; int error; error = xrep_bmap_sort_records(rb); if (error) return error; /* * Prepare to construct the new fork by initializing the new btree * structure and creating a fake ifork in the ifakeroot structure. */ xfs_rmap_ino_bmbt_owner(&oinfo, sc->ip->i_ino, rb->whichfork); error = xrep_newbt_init_inode(&rb->new_bmapbt, sc, rb->whichfork, &oinfo); if (error) return error; rb->new_bmapbt.bload.get_records = xrep_bmap_get_records; rb->new_bmapbt.bload.claim_block = xrep_bmap_claim_block; rb->new_bmapbt.bload.iroot_size = xrep_bmap_iroot_size; /* * Allocate a new bmap btree cursor for reloading an inode block mapping * data structure. */ bmap_cur = xfs_bmbt_init_cursor(sc->mp, NULL, sc->ip, XFS_STAGING_FORK); xfs_btree_stage_ifakeroot(bmap_cur, ifake); /* * Figure out the size and format of the new fork, then fill it with * all the bmap records we've found. Join the inode to the transaction * so that we can roll the transaction while holding the inode locked. */ if (rb->real_mappings <= XFS_IFORK_MAXEXT(sc->ip, rb->whichfork)) { ifake->if_fork->if_format = XFS_DINODE_FMT_EXTENTS; error = xrep_bmap_extents_load(rb); } else { ifake->if_fork->if_format = XFS_DINODE_FMT_BTREE; error = xrep_bmap_btree_load(rb, bmap_cur); } if (error) goto err_cur; /* * Install the new fork in the inode. After this point the old mapping * data are no longer accessible and the new tree is live. We delete * the cursor immediately after committing the staged root because the * staged fork might be in extents format. */ xfs_bmbt_commit_staged_btree(bmap_cur, sc->tp, rb->whichfork); xfs_btree_del_cursor(bmap_cur, 0); /* Reset the inode counters now that we've changed the fork. */ error = xrep_bmap_reset_counters(rb); if (error) goto err_newbt; /* Dispose of any unused blocks and the accounting information. */ error = xrep_newbt_commit(&rb->new_bmapbt); if (error) return error; return xrep_roll_trans(sc); err_cur: if (bmap_cur) xfs_btree_del_cursor(bmap_cur, error); err_newbt: xrep_newbt_cancel(&rb->new_bmapbt); return error; } /* * Now that we've logged the new inode btree, invalidate all of the old blocks * and free them, if there were any. */ STATIC int xrep_bmap_remove_old_tree( struct xrep_bmap *rb) { struct xfs_scrub *sc = rb->sc; struct xfs_owner_info oinfo; /* 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. */ STATIC int xrep_bmap_check_inputs( struct xfs_scrub *sc, int whichfork) { struct xfs_ifork *ifp = xfs_ifork_ptr(sc->ip, whichfork); ASSERT(whichfork == XFS_DATA_FORK || whichfork == XFS_ATTR_FORK); if (!xfs_has_rmapbt(sc->mp)) return -EOPNOTSUPP; /* No fork means nothing to rebuild. */ if (!ifp) return -ECANCELED; /* * We only know how to repair extent mappings, which is to say that we * only support extents and btree fork format. Repairs to a local * format fork require a higher level repair function, so we do not * have any work to do here. */ switch (ifp->if_format) { case XFS_DINODE_FMT_DEV: case XFS_DINODE_FMT_LOCAL: case XFS_DINODE_FMT_UUID: return -ECANCELED; case XFS_DINODE_FMT_EXTENTS: case XFS_DINODE_FMT_BTREE: break; default: return -EFSCORRUPTED; } if (whichfork == XFS_ATTR_FORK) return 0; /* 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; } /* Don't know how to rebuild realtime data forks. */ if (XFS_IS_REALTIME_INODE(sc->ip)) return -EOPNOTSUPP; return 0; } /* Set up the initial state of the reflink scan. */ static inline enum 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; /* cannot share attr fork extents */ if (whichfork != XFS_DATA_FORK) return RLS_IRRELEVANT; /* cannot share realtime extents */ if (XFS_IS_REALTIME_INODE(sc->ip)) return RLS_IRRELEVANT; return RLS_UNKNOWN; } /* Repair an inode fork. */ int xrep_bmap( struct xfs_scrub *sc, int whichfork, bool allow_unwritten) { struct xrep_bmap *rb; char *descr; unsigned int max_bmbt_recs; bool large_extcount; int error = 0; error = xrep_bmap_check_inputs(sc, whichfork); if (error == -ECANCELED) return 0; if (error) return error; rb = kzalloc(sizeof(struct xrep_bmap), XCHK_GFP_FLAGS); if (!rb) return -ENOMEM; rb->sc = sc; rb->whichfork = whichfork; rb->reflink_scan = xrep_bmap_init_reflink_scan(sc, whichfork); rb->allow_unwritten = allow_unwritten; /* 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; out_bitmap: xfsb_bitmap_destroy(&rb->old_bmbt_blocks); xfarray_destroy(rb->bmap_records); out_rb: kfree(rb); return error; } /* Repair an inode's data fork. */ int xrep_bmap_data( struct xfs_scrub *sc) { return xrep_bmap(sc, XFS_DATA_FORK, true); } /* Repair an inode's attr fork. */ int xrep_bmap_attr( struct xfs_scrub *sc) { return xrep_bmap(sc, XFS_ATTR_FORK, false); }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1