xfs: fix reflink quota reservation accounting error
authorDarrick J. Wong <darrick.wong@oracle.com>
Mon, 29 Jun 2020 21:47:18 +0000 (14:47 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 21 Aug 2020 07:48:08 +0000 (09:48 +0200)
[ Upstream commit 83895227aba1ade33e81f586aa7b6b1e143096a5 ]

Quota reservations are supposed to account for the blocks that might be
allocated due to a bmap btree split.  Reflink doesn't do this, so fix
this to make the quota accounting more accurate before we start
rearranging things.

Fixes: 862bb360ef56 ("xfs: reflink extents from one file to another")
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/xfs/xfs_reflink.c

index db7f9fdd20a30d3761a316aac0496b8377e7d049..4d37f1b59436ccffcaca2fc9935701bb77d41e92 100644 (file)
@@ -1076,6 +1076,7 @@ xfs_reflink_remap_extent(
        xfs_filblks_t           rlen;
        xfs_filblks_t           unmap_len;
        xfs_off_t               newlen;
+       int64_t                 qres;
        int                     error;
 
        unmap_len = irec->br_startoff + irec->br_blockcount - destoff;
@@ -1098,13 +1099,19 @@ xfs_reflink_remap_extent(
        xfs_ilock(ip, XFS_ILOCK_EXCL);
        xfs_trans_ijoin(tp, ip, 0);
 
-       /* If we're not just clearing space, then do we have enough quota? */
-       if (real_extent) {
-               error = xfs_trans_reserve_quota_nblks(tp, ip,
-                               irec->br_blockcount, 0, XFS_QMOPT_RES_REGBLKS);
-               if (error)
-                       goto out_cancel;
-       }
+       /*
+        * Reserve quota for this operation.  We don't know if the first unmap
+        * in the dest file will cause a bmap btree split, so we always reserve
+        * at least enough blocks for that split.  If the extent being mapped
+        * in is written, we need to reserve quota for that too.
+        */
+       qres = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK);
+       if (real_extent)
+               qres += irec->br_blockcount;
+       error = xfs_trans_reserve_quota_nblks(tp, ip, qres, 0,
+                       XFS_QMOPT_RES_REGBLKS);
+       if (error)
+               goto out_cancel;
 
        trace_xfs_reflink_remap(ip, irec->br_startoff,
                                irec->br_blockcount, irec->br_startblock);