Skip to content

Commit 0c4da70

Browse files
osandovdjwong
authored andcommitted
xfs: fix realtime file data space leak
Realtime files in XFS allocate extents in rextsize units. However, the written/unwritten state of those extents is still tracked in blocksize units. Therefore, a realtime file can be split up into written and unwritten extents that are not necessarily aligned to the realtime extent size. __xfs_bunmapi() has some logic to handle these various corner cases. Consider how it handles the following case: 1. The last extent is unwritten. 2. The last extent is smaller than the realtime extent size. 3. startblock of the last extent is not aligned to the realtime extent size, but startblock + blockcount is. In this case, __xfs_bunmapi() calls xfs_bmap_add_extent_unwritten_real() to set the second-to-last extent to unwritten. This should merge the last and second-to-last extents, so __xfs_bunmapi() moves on to the second-to-last extent. However, if the size of the last and second-to-last extents combined is greater than MAXEXTLEN, xfs_bmap_add_extent_unwritten_real() does not merge the two extents. When that happens, __xfs_bunmapi() skips past the last extent without unmapping it, thus leaking the space. Fix it by only unwriting the minimum amount needed to align the last extent to the realtime extent size, which is guaranteed to merge with the last extent. Signed-off-by: Omar Sandoval <[email protected]> Reviewed-by: Darrick J. Wong <[email protected]> Signed-off-by: Darrick J. Wong <[email protected]>
1 parent 8feb473 commit 0c4da70

File tree

1 file changed

+14
-11
lines changed

1 file changed

+14
-11
lines changed

fs/xfs/libxfs/xfs_bmap.c

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5480,16 +5480,17 @@ __xfs_bunmapi(
54805480
}
54815481
div_u64_rem(del.br_startblock, mp->m_sb.sb_rextsize, &mod);
54825482
if (mod) {
5483+
xfs_extlen_t off = mp->m_sb.sb_rextsize - mod;
5484+
54835485
/*
54845486
* Realtime extent is lined up at the end but not
54855487
* at the front. We'll get rid of full extents if
54865488
* we can.
54875489
*/
5488-
mod = mp->m_sb.sb_rextsize - mod;
5489-
if (del.br_blockcount > mod) {
5490-
del.br_blockcount -= mod;
5491-
del.br_startoff += mod;
5492-
del.br_startblock += mod;
5490+
if (del.br_blockcount > off) {
5491+
del.br_blockcount -= off;
5492+
del.br_startoff += off;
5493+
del.br_startblock += off;
54935494
} else if (del.br_startoff == start &&
54945495
(del.br_state == XFS_EXT_UNWRITTEN ||
54955496
tp->t_blk_res == 0)) {
@@ -5507,6 +5508,7 @@ __xfs_bunmapi(
55075508
continue;
55085509
} else if (del.br_state == XFS_EXT_UNWRITTEN) {
55095510
struct xfs_bmbt_irec prev;
5511+
xfs_fileoff_t unwrite_start;
55105512

55115513
/*
55125514
* This one is already unwritten.
@@ -5520,12 +5522,13 @@ __xfs_bunmapi(
55205522
ASSERT(!isnullstartblock(prev.br_startblock));
55215523
ASSERT(del.br_startblock ==
55225524
prev.br_startblock + prev.br_blockcount);
5523-
if (prev.br_startoff < start) {
5524-
mod = start - prev.br_startoff;
5525-
prev.br_blockcount -= mod;
5526-
prev.br_startblock += mod;
5527-
prev.br_startoff = start;
5528-
}
5525+
unwrite_start = max3(start,
5526+
del.br_startoff - mod,
5527+
prev.br_startoff);
5528+
mod = unwrite_start - prev.br_startoff;
5529+
prev.br_startoff = unwrite_start;
5530+
prev.br_startblock += mod;
5531+
prev.br_blockcount -= mod;
55295532
prev.br_state = XFS_EXT_UNWRITTEN;
55305533
error = xfs_bmap_add_extent_unwritten_real(tp,
55315534
ip, whichfork, &icur, &cur,

0 commit comments

Comments
 (0)