diff options
author | Akira Fujita <a-fujita@rs.jp.nec.com> | 2009-09-16 14:25:39 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2009-09-16 14:25:39 -0400 |
commit | c40ce3c9ea97425a12d7e44031a98fe50add6fc1 (patch) | |
tree | 2150e1dbbcbf42c8293a887976a648fceaf8da73 /fs/ext4 | |
parent | 347fa6f1c7cb5df2b38d3c9167cfe242ce0cd1da (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>
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/move_extent.c | 46 |
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 | */ |
601 | static void | 603 | static int |
602 | mext_calc_swap_extents(struct ext4_extent *tmp_dext, | 604 | mext_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 | ||
768 | out: | 788 | out: |
@@ -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) { |