aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBob Peterson <rpeterso@redhat.com>2017-08-30 10:26:09 -0400
committerBob Peterson <rpeterso@redhat.com>2017-08-30 14:29:22 -0400
commitc4a9d1892f1ce6fe040b717b68bd21e689cc2410 (patch)
tree318a7c5a9566218c32e543a28b7e6e3d734f5fcf
parentd296b15ed58231bd991c0fb0f3592d595539bcd1 (diff)
GFS2: Fix non-recursive truncate bug
Before this patch if you truncated a file to a smaller size it wasn't freeing all the blocks properly. There are two reasons. First, the metapath comparison was not comparing previous heights. I added a function, mp_eq_to_hgt, which checks the metapath at all heights prior to the target height. Second, in function find_nonnull_ptr, it needed to zero out all pointers for heights following the target height. Translated into decimal integer terms, this way a number like 299, when incremented, becomes 300, not 399. The 2 gets incremented to 3, and the following digits need to be reset. These two things allow the truncate state machine to properly find the blocks it needs to delete. Signed-off-by: Bob Peterson <rpeterso@redhat.com>
-rw-r--r--fs/gfs2/bmap.c19
1 files changed, 16 insertions, 3 deletions
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index fa3ea29f39cf..3dd0cceefa43 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -1104,8 +1104,15 @@ static bool find_nonnull_ptr(struct gfs2_sbd *sdp, struct metapath *mp,
1104 1104
1105 while (true) { 1105 while (true) {
1106 ptr = metapointer(h, mp); 1106 ptr = metapointer(h, mp);
1107 if (*ptr) /* if we have a non-null pointer */ 1107 if (*ptr) { /* if we have a non-null pointer */
1108 /* Now zero the metapath after the current height. */
1109 h++;
1110 if (h < GFS2_MAX_META_HEIGHT)
1111 memset(&mp->mp_list[h], 0,
1112 (GFS2_MAX_META_HEIGHT - h) *
1113 sizeof(mp->mp_list[0]));
1108 return true; 1114 return true;
1115 }
1109 1116
1110 if (mp->mp_list[h] < ptrs) 1117 if (mp->mp_list[h] < ptrs)
1111 mp->mp_list[h]++; 1118 mp->mp_list[h]++;
@@ -1121,6 +1128,13 @@ enum dealloc_states {
1121 DEALLOC_DONE = 3, /* process complete */ 1128 DEALLOC_DONE = 3, /* process complete */
1122}; 1129};
1123 1130
1131static bool mp_eq_to_hgt(struct metapath *mp, __u16 *nbof, unsigned int h)
1132{
1133 if (memcmp(mp->mp_list, nbof, h * sizeof(mp->mp_list[0])))
1134 return false;
1135 return true;
1136}
1137
1124/** 1138/**
1125 * trunc_dealloc - truncate a file down to a desired size 1139 * trunc_dealloc - truncate a file down to a desired size
1126 * @ip: inode to truncate 1140 * @ip: inode to truncate
@@ -1198,8 +1212,7 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
1198 /* If we're truncating to a non-zero size and the mp is 1212 /* If we're truncating to a non-zero size and the mp is
1199 at the beginning of file for the strip height, we 1213 at the beginning of file for the strip height, we
1200 need to preserve the first metadata pointer. */ 1214 need to preserve the first metadata pointer. */
1201 preserve1 = (newsize && 1215 preserve1 = (newsize && mp_eq_to_hgt(&mp, nbof, mp_h));
1202 (mp.mp_list[mp_h] == nbof[mp_h]));
1203 bh = mp.mp_bh[mp_h]; 1216 bh = mp.mp_bh[mp_h];
1204 gfs2_assert_withdraw(sdp, bh); 1217 gfs2_assert_withdraw(sdp, bh);
1205 if (gfs2_assert_withdraw(sdp, 1218 if (gfs2_assert_withdraw(sdp,