aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAkira Fujita <a-fujita@rs.jp.nec.com>2009-09-16 14:25:39 -0400
committerTheodore Ts'o <tytso@mit.edu>2009-09-16 14:25:39 -0400
commitc40ce3c9ea97425a12d7e44031a98fe50add6fc1 (patch)
tree2150e1dbbcbf42c8293a887976a648fceaf8da73
parent347fa6f1c7cb5df2b38d3c9167cfe242ce0cd1da (diff)
ext4: Fix different block exchange issue in EXT4_IOC_MOVE_EXT
If logical block offset of original file which is passed to EXT4_IOC_MOVE_EXT is different from donor file's, a calculation error occurs in ext4_calc_swap_extents(), therefore wrong block is exchanged between original file and donor file. As a result, we hit ext4_error() in check_block_validity(). To detect the logical offset difference in EXT4_IOC_MOVE_EXT, add checks to mext_calc_swap_extents() and handle it as error, since data exchange must be done between the same blocks in EXT4_IOC_MOVE_EXT. Reported-by: Peng Tao <bergwolf@gmail.com> Signed-off-by: Akira Fujita <a-fujita@rs.jp.nec.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r--fs/ext4/move_extent.c46
1 files changed, 37 insertions, 9 deletions
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
index 1c509d549137..1f027b1ec430 100644
--- a/fs/ext4/move_extent.c
+++ b/fs/ext4/move_extent.c
@@ -597,8 +597,10 @@ out:
597 * @orig_off: block offset of original inode 597 * @orig_off: block offset of original inode
598 * @donor_off: block offset of donor inode 598 * @donor_off: block offset of donor inode
599 * @max_count: the maximun length of extents 599 * @max_count: the maximun length of extents
600 *
601 * Return 0 on success, or a negative error value on failure.
600 */ 602 */
601static void 603static int
602mext_calc_swap_extents(struct ext4_extent *tmp_dext, 604mext_calc_swap_extents(struct ext4_extent *tmp_dext,
603 struct ext4_extent *tmp_oext, 605 struct ext4_extent *tmp_oext,
604 ext4_lblk_t orig_off, ext4_lblk_t donor_off, 606 ext4_lblk_t orig_off, ext4_lblk_t donor_off,
@@ -607,6 +609,19 @@ mext_calc_swap_extents(struct ext4_extent *tmp_dext,
607 ext4_lblk_t diff, orig_diff; 609 ext4_lblk_t diff, orig_diff;
608 struct ext4_extent dext_old, oext_old; 610 struct ext4_extent dext_old, oext_old;
609 611
612 BUG_ON(orig_off != donor_off);
613
614 /* original and donor extents have to cover the same block offset */
615 if (orig_off < le32_to_cpu(tmp_oext->ee_block) ||
616 le32_to_cpu(tmp_oext->ee_block) +
617 ext4_ext_get_actual_len(tmp_oext) - 1 < orig_off)
618 return -ENODATA;
619
620 if (orig_off < le32_to_cpu(tmp_dext->ee_block) ||
621 le32_to_cpu(tmp_dext->ee_block) +
622 ext4_ext_get_actual_len(tmp_dext) - 1 < orig_off)
623 return -ENODATA;
624
610 dext_old = *tmp_dext; 625 dext_old = *tmp_dext;
611 oext_old = *tmp_oext; 626 oext_old = *tmp_oext;
612 627
@@ -634,6 +649,8 @@ mext_calc_swap_extents(struct ext4_extent *tmp_dext,
634 649
635 copy_extent_status(&oext_old, tmp_dext); 650 copy_extent_status(&oext_old, tmp_dext);
636 copy_extent_status(&dext_old, tmp_oext); 651 copy_extent_status(&dext_old, tmp_oext);
652
653 return 0;
637} 654}
638 655
639/** 656/**
@@ -690,8 +707,10 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
690 dext = donor_path[depth].p_ext; 707 dext = donor_path[depth].p_ext;
691 tmp_dext = *dext; 708 tmp_dext = *dext;
692 709
693 mext_calc_swap_extents(&tmp_dext, &tmp_oext, orig_off, 710 err = mext_calc_swap_extents(&tmp_dext, &tmp_oext, orig_off,
694 donor_off, count); 711 donor_off, count);
712 if (err)
713 goto out;
695 714
696 /* Loop for the donor extents */ 715 /* Loop for the donor extents */
697 while (1) { 716 while (1) {
@@ -760,9 +779,10 @@ mext_replace_branches(handle_t *handle, struct inode *orig_inode,
760 } 779 }
761 tmp_dext = *dext; 780 tmp_dext = *dext;
762 781
763 mext_calc_swap_extents(&tmp_dext, &tmp_oext, orig_off, 782 err = mext_calc_swap_extents(&tmp_dext, &tmp_oext, orig_off,
764 donor_off, 783 donor_off, count - replaced_count);
765 count - replaced_count); 784 if (err)
785 goto out;
766 } 786 }
767 787
768out: 788out:
@@ -1243,11 +1263,15 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
1243 ext_cur = holecheck_path[depth].p_ext; 1263 ext_cur = holecheck_path[depth].p_ext;
1244 1264
1245 /* 1265 /*
1246 * Get proper extent whose ee_block is beyond block_start 1266 * Get proper starting location of block replacement if block_start was
1247 * if block_start was within the hole. 1267 * within the hole.
1248 */ 1268 */
1249 if (le32_to_cpu(ext_cur->ee_block) + 1269 if (le32_to_cpu(ext_cur->ee_block) +
1250 ext4_ext_get_actual_len(ext_cur) - 1 < block_start) { 1270 ext4_ext_get_actual_len(ext_cur) - 1 < block_start) {
1271 /*
1272 * The hole exists between extents or the tail of
1273 * original file.
1274 */
1251 last_extent = mext_next_extent(orig_inode, 1275 last_extent = mext_next_extent(orig_inode,
1252 holecheck_path, &ext_cur); 1276 holecheck_path, &ext_cur);
1253 if (last_extent < 0) { 1277 if (last_extent < 0) {
@@ -1260,8 +1284,12 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp,
1260 ret1 = last_extent; 1284 ret1 = last_extent;
1261 goto out; 1285 goto out;
1262 } 1286 }
1263 } 1287 seq_start = le32_to_cpu(ext_cur->ee_block);
1264 seq_start = block_start; 1288 } else if (le32_to_cpu(ext_cur->ee_block) > block_start)
1289 /* The hole exists at the beginning of original file. */
1290 seq_start = le32_to_cpu(ext_cur->ee_block);
1291 else
1292 seq_start = block_start;
1265 1293
1266 /* No blocks within the specified range. */ 1294 /* No blocks within the specified range. */
1267 if (le32_to_cpu(ext_cur->ee_block) > block_end) { 1295 if (le32_to_cpu(ext_cur->ee_block) > block_end) {