| From c06344939422bbd032ac967223a7863de57496b5 Mon Sep 17 00:00:00 2001 |
| From: Eric Whitney <enwlinux@gmail.com> |
| Date: Thu, 13 Mar 2014 23:34:16 -0400 |
| Subject: ext4: fix partial cluster handling for bigalloc file systems |
| |
| From: Eric Whitney <enwlinux@gmail.com> |
| |
| commit c06344939422bbd032ac967223a7863de57496b5 upstream. |
| |
| Commit 9cb00419fa, which enables hole punching for bigalloc file |
| systems, exposed a bug introduced by commit 6ae06ff51e in an earlier |
| release. When run on a bigalloc file system, xfstests generic/013, 068, |
| 075, 083, 091, 100, 112, 127, 263, 269, and 270 fail with e2fsck errors |
| or cause kernel error messages indicating that previously freed blocks |
| are being freed again. |
| |
| The latter commit optimizes the selection of the starting extent in |
| ext4_ext_rm_leaf() when hole punching by beginning with the extent |
| supplied in the path argument rather than with the last extent in the |
| leaf node (as is still done when truncating). However, the code in |
| rm_leaf that initially sets partial_cluster to track cluster sharing on |
| extent boundaries is only guaranteed to run if rm_leaf starts with the |
| last node in the leaf. Consequently, partial_cluster is not correctly |
| initialized when hole punching, and a cluster on the boundary of a |
| punched region that should be retained may instead be deallocated. |
| |
| Signed-off-by: Eric Whitney <enwlinux@gmail.com> |
| Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/ext4/extents.c | 21 +++++++++++++++++++++ |
| 1 file changed, 21 insertions(+) |
| |
| --- a/fs/ext4/extents.c |
| +++ b/fs/ext4/extents.c |
| @@ -2585,6 +2585,27 @@ ext4_ext_rm_leaf(handle_t *handle, struc |
| ex_ee_block = le32_to_cpu(ex->ee_block); |
| ex_ee_len = ext4_ext_get_actual_len(ex); |
| |
| + /* |
| + * If we're starting with an extent other than the last one in the |
| + * node, we need to see if it shares a cluster with the extent to |
| + * the right (towards the end of the file). If its leftmost cluster |
| + * is this extent's rightmost cluster and it is not cluster aligned, |
| + * we'll mark it as a partial that is not to be deallocated. |
| + */ |
| + |
| + if (ex != EXT_LAST_EXTENT(eh)) { |
| + ext4_fsblk_t current_pblk, right_pblk; |
| + long long current_cluster, right_cluster; |
| + |
| + current_pblk = ext4_ext_pblock(ex) + ex_ee_len - 1; |
| + current_cluster = (long long)EXT4_B2C(sbi, current_pblk); |
| + right_pblk = ext4_ext_pblock(ex + 1); |
| + right_cluster = (long long)EXT4_B2C(sbi, right_pblk); |
| + if (current_cluster == right_cluster && |
| + EXT4_PBLK_COFF(sbi, right_pblk)) |
| + *partial_cluster = -right_cluster; |
| + } |
| + |
| trace_ext4_ext_rm_leaf(inode, start, ex, *partial_cluster); |
| |
| while (ex >= EXT_FIRST_EXTENT(eh) && |