diff options
Diffstat (limited to 'fs/xfs/xfs_bmap_util.c')
-rw-r--r-- | fs/xfs/xfs_bmap_util.c | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index 2774939e176d..bcac7530d1ac 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c @@ -1358,6 +1358,8 @@ xfs_swap_extent_rmap( /* Unmap the old blocks in the source file. */ while (tirec.br_blockcount) { + int64_t ip_delta = 0, tip_delta = 0; + ASSERT(tp->t_firstblock == NULLFSBLOCK); trace_xfs_swap_extent_rmap_remap_piece(tip, &tirec); @@ -1388,6 +1390,23 @@ xfs_swap_extent_rmap( irec.br_blockcount); trace_xfs_swap_extent_rmap_remap_piece(tip, &uirec); + /* Update quota accounting. */ + if (xfs_bmap_is_mapped_extent(&irec)) { + tip_delta += irec.br_blockcount; + ip_delta -= irec.br_blockcount; + } + if (xfs_bmap_is_mapped_extent(&uirec)) { + tip_delta -= uirec.br_blockcount; + ip_delta += uirec.br_blockcount; + } + + if (tip_delta) + xfs_trans_mod_dquot_byino(tp, tip, + XFS_TRANS_DQ_BCOUNT, tip_delta); + if (ip_delta) + xfs_trans_mod_dquot_byino(tp, ip, + XFS_TRANS_DQ_BCOUNT, ip_delta); + /* Remove the mapping from the donor file. */ xfs_bmap_unmap_extent(tp, tip, &uirec); @@ -1435,6 +1454,7 @@ xfs_swap_extent_forks( { xfs_filblks_t aforkblks = 0; xfs_filblks_t taforkblks = 0; + int64_t temp_blks; xfs_extnum_t junk; uint64_t tmp; int error; @@ -1476,6 +1496,15 @@ xfs_swap_extent_forks( */ swap(ip->i_df, tip->i_df); + /* Update quota accounting. */ + temp_blks = tip->i_d.di_nblocks - taforkblks + aforkblks; + xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, + temp_blks - ip->i_d.di_nblocks); + + temp_blks = ip->i_d.di_nblocks + taforkblks - aforkblks; + xfs_trans_mod_dquot_byino(tp, tip, XFS_TRANS_DQ_BCOUNT, + temp_blks - tip->i_d.di_nblocks); + /* * Fix the on-disk inode values */ @@ -1566,6 +1595,93 @@ xfs_swap_change_owner( return error; } +/* + * Obtain a quota reservation to make sure we don't hit EDQUOT. We can skip + * this if quota enforcement is disabled or if both inodes' dquots are the + * same. + */ +STATIC int +xfs_swap_extents_prep_quota( + struct xfs_trans *tp, + struct xfs_inode *ip, + struct xfs_inode *tip) +{ + struct xfs_mount *mp = ip->i_mount; + xfs_filblks_t aforkblks = 0; + xfs_filblks_t taforkblks = 0; + xfs_filblks_t ip_mapped, tip_mapped; + xfs_extnum_t junk; + int error; + + /* + * Don't bother with a quota reservation if we're not enforcing them + * or the two inodes have the same dquots. + */ + if (!(mp->m_qflags & XFS_ALL_QUOTA_ENFD) || + (ip->i_udquot == tip->i_udquot && + ip->i_gdquot == tip->i_gdquot && + ip->i_pdquot == tip->i_pdquot)) + return 0; + + /* + * Count the number of extended attribute blocks + */ + if ( ((XFS_IFORK_Q(ip) != 0) && (ip->i_d.di_anextents > 0)) && + (ip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) { + error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK, &junk, + &aforkblks); + if (error) + return error; + } + if ( ((XFS_IFORK_Q(tip) != 0) && (tip->i_d.di_anextents > 0)) && + (tip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) { + error = xfs_bmap_count_blocks(tp, tip, XFS_ATTR_FORK, &junk, + &taforkblks); + if (error) + return error; + } + + /* Figure out how many blocks we'll move out of each file. */ + ip_mapped = ip->i_d.di_nblocks - aforkblks; + tip_mapped = tip->i_d.di_nblocks - taforkblks; + + /* + * For each file, compute the net gain in the number of blocks that + * will be mapped into that file and reserve that much quota. The + * quota counts must be able to absorb at least that much space. + */ + if (tip_mapped > ip_mapped) { + error = xfs_trans_reserve_quota_nblks(tp, ip, + tip_mapped - ip_mapped, 0, + XFS_QMOPT_RES_REGBLKS); + if (error) + return error; + } + + if (ip_mapped > tip_mapped) { + error = xfs_trans_reserve_quota_nblks(tp, tip, + ip_mapped - tip_mapped, 0, + XFS_QMOPT_RES_REGBLKS); + if (error) + return error; + } + + /* + * For each file, forcibly reserve the gross gain in mapped blocks so + * that we don't trip over any quota block reservation assertions. + * We must reserve the gross gain because the quota code subtracts from + * bcount the number of blocks that we unmap; it does not add that + * quantity back to the quota block reservation. + */ + error = xfs_trans_reserve_quota_nblks(tp, ip, ip_mapped, 0, + XFS_QMOPT_FORCE_RES | XFS_QMOPT_RES_REGBLKS); + if (error) + return error; + + return xfs_trans_reserve_quota_nblks(tp, tip, tip_mapped, 0, + XFS_QMOPT_FORCE_RES | XFS_QMOPT_RES_REGBLKS); +} + int xfs_swap_extents( struct xfs_inode *ip, /* target inode */ @@ -1703,6 +1819,15 @@ xfs_swap_extents( } /* + * Reserve ourselves some quota if any of them are in enforcing mode. + * In theory we only need enough to satisfy the change in the number + * of blocks between the two ranges being remapped. + */ + error = xfs_swap_extents_prep_quota(tp, ip, tip); + if (error) + goto out_trans_cancel; + + /* * Note the trickiness in setting the log flags - we set the owner log * flag on the opposite inode (i.e. the inode we are setting the new * owner to be) because once we swap the forks and log that, log |