diff options
Diffstat (limited to 'fs/xfs/libxfs')
-rw-r--r-- | fs/xfs/libxfs/xfs_bmap.c | 187 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_btree.c | 10 | ||||
-rw-r--r-- | fs/xfs/libxfs/xfs_btree.h | 9 |
3 files changed, 91 insertions, 115 deletions
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index f16c6cdf2c58..77e52874e8c5 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -1155,6 +1155,65 @@ trans_cancel: * Internal and external extent tree search functions. */ +struct xfs_iread_state { + struct xfs_iext_cursor icur; + xfs_extnum_t loaded; +}; + +/* Stuff every bmbt record from this block into the incore extent map. */ +static int +xfs_iread_bmbt_block( + struct xfs_btree_cur *cur, + int level, + void *priv) +{ + struct xfs_iread_state *ir = priv; + struct xfs_mount *mp = cur->bc_mp; + struct xfs_inode *ip = cur->bc_private.b.ip; + struct xfs_btree_block *block; + struct xfs_buf *bp; + struct xfs_bmbt_rec *frp; + xfs_extnum_t num_recs; + xfs_extnum_t j; + int whichfork = cur->bc_private.b.whichfork; + + block = xfs_btree_get_block(cur, level, &bp); + + /* Abort if we find more records than nextents. */ + num_recs = xfs_btree_get_numrecs(block); + if (unlikely(ir->loaded + num_recs > + XFS_IFORK_NEXTENTS(ip, whichfork))) { + xfs_warn(ip->i_mount, "corrupt dinode %llu, (btree extents).", + (unsigned long long)ip->i_ino); + xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, block, + sizeof(*block), __this_address); + return -EFSCORRUPTED; + } + + /* Copy records into the incore cache. */ + frp = XFS_BMBT_REC_ADDR(mp, block, 1); + for (j = 0; j < num_recs; j++, frp++, ir->loaded++) { + struct xfs_bmbt_irec new; + xfs_failaddr_t fa; + + xfs_bmbt_disk_get_all(frp, &new); + fa = xfs_bmap_validate_extent(ip, whichfork, &new); + if (fa) { + xfs_inode_verifier_error(ip, -EFSCORRUPTED, + "xfs_iread_extents(2)", frp, + sizeof(*frp), fa); + return -EFSCORRUPTED; + } + xfs_iext_insert(ip, &ir->icur, &new, + xfs_bmap_fork_to_state(whichfork)); + trace_xfs_read_extent(ip, &ir->icur, + xfs_bmap_fork_to_state(whichfork), _THIS_IP_); + xfs_iext_next(XFS_IFORK_PTR(ip, whichfork), &ir->icur); + } + + return 0; +} + /* * Read in extents from a btree-format inode. */ @@ -1164,134 +1223,38 @@ xfs_iread_extents( struct xfs_inode *ip, int whichfork) { - struct xfs_mount *mp = ip->i_mount; - int state = xfs_bmap_fork_to_state(whichfork); + struct xfs_iread_state ir; struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); - xfs_extnum_t nextents = XFS_IFORK_NEXTENTS(ip, whichfork); - struct xfs_btree_block *block = ifp->if_broot; - struct xfs_iext_cursor icur; - struct xfs_bmbt_irec new; - xfs_fsblock_t bno; - struct xfs_buf *bp; - xfs_extnum_t i, j; - int level; - __be64 *pp; + struct xfs_mount *mp = ip->i_mount; + struct xfs_btree_cur *cur; int error; ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); if (unlikely(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE)) { XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); - return -EFSCORRUPTED; - } - - /* - * Root level must use BMAP_BROOT_PTR_ADDR macro to get ptr out. - */ - level = be16_to_cpu(block->bb_level); - if (unlikely(level == 0)) { - XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); - return -EFSCORRUPTED; - } - pp = XFS_BMAP_BROOT_PTR_ADDR(mp, block, 1, ifp->if_broot_bytes); - bno = be64_to_cpu(*pp); - - /* - * Go down the tree until leaf level is reached, following the first - * pointer (leftmost) at each level. - */ - while (level-- > 0) { - error = xfs_btree_read_bufl(mp, tp, bno, &bp, - XFS_BMAP_BTREE_REF, &xfs_bmbt_buf_ops); - if (error) - goto out; - block = XFS_BUF_TO_BLOCK(bp); - if (level == 0) - break; - pp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[1]); - bno = be64_to_cpu(*pp); - XFS_WANT_CORRUPTED_GOTO(mp, - xfs_verify_fsbno(mp, bno), out_brelse); - xfs_trans_brelse(tp, bp); + error = -EFSCORRUPTED; + goto out; } - /* - * Here with bp and block set to the leftmost leaf node in the tree. - */ - i = 0; - xfs_iext_first(ifp, &icur); - - /* - * Loop over all leaf nodes. Copy information to the extent records. - */ - for (;;) { - xfs_bmbt_rec_t *frp; - xfs_fsblock_t nextbno; - xfs_extnum_t num_recs; - - num_recs = xfs_btree_get_numrecs(block); - if (unlikely(i + num_recs > nextents)) { - xfs_warn(ip->i_mount, - "corrupt dinode %Lu, (btree extents).", - (unsigned long long) ip->i_ino); - xfs_inode_verifier_error(ip, -EFSCORRUPTED, - __func__, block, sizeof(*block), - __this_address); - error = -EFSCORRUPTED; - goto out_brelse; - } - /* - * Read-ahead the next leaf block, if any. - */ - nextbno = be64_to_cpu(block->bb_u.l.bb_rightsib); - if (nextbno != NULLFSBLOCK) - xfs_btree_reada_bufl(mp, nextbno, 1, - &xfs_bmbt_buf_ops); - /* - * Copy records into the extent records. - */ - frp = XFS_BMBT_REC_ADDR(mp, block, 1); - for (j = 0; j < num_recs; j++, frp++, i++) { - xfs_failaddr_t fa; - - xfs_bmbt_disk_get_all(frp, &new); - fa = xfs_bmap_validate_extent(ip, whichfork, &new); - if (fa) { - error = -EFSCORRUPTED; - xfs_inode_verifier_error(ip, error, - "xfs_iread_extents(2)", - frp, sizeof(*frp), fa); - goto out_brelse; - } - xfs_iext_insert(ip, &icur, &new, state); - trace_xfs_read_extent(ip, &icur, state, _THIS_IP_); - xfs_iext_next(ifp, &icur); - } - xfs_trans_brelse(tp, bp); - bno = nextbno; - /* - * If we've reached the end, stop. - */ - if (bno == NULLFSBLOCK) - break; - error = xfs_btree_read_bufl(mp, tp, bno, &bp, - XFS_BMAP_BTREE_REF, &xfs_bmbt_buf_ops); - if (error) - goto out; - block = XFS_BUF_TO_BLOCK(bp); - } + ir.loaded = 0; + xfs_iext_first(ifp, &ir.icur); + cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork); + error = xfs_btree_visit_blocks(cur, xfs_iread_bmbt_block, + XFS_BTREE_VISIT_RECORDS, &ir); + xfs_btree_del_cursor(cur, error); + if (error) + goto out; - if (i != XFS_IFORK_NEXTENTS(ip, whichfork)) { + if (ir.loaded != XFS_IFORK_NEXTENTS(ip, whichfork)) { + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); error = -EFSCORRUPTED; goto out; } - ASSERT(i == xfs_iext_count(ifp)); + ASSERT(ir.loaded == xfs_iext_count(ifp)); ifp->if_flags |= XFS_IFEXTENTS; return 0; - -out_brelse: - xfs_trans_brelse(tp, bp); out: xfs_iext_destroy(ifp); return error; diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c index 71de937f9e64..4fd89c80c821 100644 --- a/fs/xfs/libxfs/xfs_btree.c +++ b/fs/xfs/libxfs/xfs_btree.c @@ -4286,6 +4286,7 @@ int xfs_btree_visit_blocks( struct xfs_btree_cur *cur, xfs_btree_visit_blocks_fn fn, + unsigned int flags, void *data) { union xfs_btree_ptr lptr; @@ -4311,6 +4312,11 @@ xfs_btree_visit_blocks( /* save for the next iteration of the loop */ xfs_btree_copy_ptrs(cur, &lptr, ptr, 1); + + if (!(flags & XFS_BTREE_VISIT_LEAVES)) + continue; + } else if (!(flags & XFS_BTREE_VISIT_RECORDS)) { + continue; } /* for each buffer in the level */ @@ -4413,7 +4419,7 @@ xfs_btree_change_owner( bbcoi.buffer_list = buffer_list; return xfs_btree_visit_blocks(cur, xfs_btree_block_change_owner, - &bbcoi); + XFS_BTREE_VISIT_ALL, &bbcoi); } /* Verify the v5 fields of a long-format btree block. */ @@ -4865,7 +4871,7 @@ xfs_btree_count_blocks( { *blocks = 0; return xfs_btree_visit_blocks(cur, xfs_btree_count_blocks_helper, - blocks); + XFS_BTREE_VISIT_ALL, blocks); } /* Compare two btree pointers. */ diff --git a/fs/xfs/libxfs/xfs_btree.h b/fs/xfs/libxfs/xfs_btree.h index b4e3ec1d7ff9..6670120cd690 100644 --- a/fs/xfs/libxfs/xfs_btree.h +++ b/fs/xfs/libxfs/xfs_btree.h @@ -485,8 +485,15 @@ int xfs_btree_query_all(struct xfs_btree_cur *cur, xfs_btree_query_range_fn fn, typedef int (*xfs_btree_visit_blocks_fn)(struct xfs_btree_cur *cur, int level, void *data); +/* Visit record blocks. */ +#define XFS_BTREE_VISIT_RECORDS (1 << 0) +/* Visit leaf blocks. */ +#define XFS_BTREE_VISIT_LEAVES (1 << 1) +/* Visit all blocks. */ +#define XFS_BTREE_VISIT_ALL (XFS_BTREE_VISIT_RECORDS | \ + XFS_BTREE_VISIT_LEAVES) int xfs_btree_visit_blocks(struct xfs_btree_cur *cur, - xfs_btree_visit_blocks_fn fn, void *data); + xfs_btree_visit_blocks_fn fn, unsigned int flags, void *data); int xfs_btree_count_blocks(struct xfs_btree_cur *cur, xfs_extlen_t *blocks); |