Btrfs: make sure not to return overlapping extents to fiemap

The btrfs fiemap code was incorrectly returning duplicate or overlapping
extents in some cases. cp was blindly trusting this result and we would
end up with a destination file that was bigger than the original because
some bytes were copied twice.

The fix here adjusts our offsets to make sure we're always moving
forward in the fiemap results.

Signed-off-by: Chris Mason <chris.mason@oracle.com>

+27 -6
+27 -6
fs/btrfs/extent_io.c
··· 3046 3046 } 3047 3047 3048 3048 while (!end) { 3049 - off = extent_map_end(em); 3050 - if (off >= max) 3051 - end = 1; 3049 + u64 offset_in_extent; 3052 3050 3053 - em_start = em->start; 3054 - em_len = em->len; 3051 + /* break if the extent we found is outside the range */ 3052 + if (em->start >= max || extent_map_end(em) < off) 3053 + break; 3054 + 3055 + /* 3056 + * get_extent may return an extent that starts before our 3057 + * requested range. We have to make sure the ranges 3058 + * we return to fiemap always move forward and don't 3059 + * overlap, so adjust the offsets here 3060 + */ 3061 + em_start = max(em->start, off); 3062 + 3063 + /* 3064 + * record the offset from the start of the extent 3065 + * for adjusting the disk offset below 3066 + */ 3067 + offset_in_extent = em_start - em->start; 3055 3068 em_end = extent_map_end(em); 3069 + em_len = em_end - em_start; 3056 3070 emflags = em->flags; 3057 3071 disko = 0; 3058 3072 flags = 0; 3073 + 3074 + /* 3075 + * bump off for our next call to get_extent 3076 + */ 3077 + off = extent_map_end(em); 3078 + if (off >= max) 3079 + end = 1; 3059 3080 3060 3081 if (em->block_start == EXTENT_MAP_LAST_BYTE) { 3061 3082 end = 1; ··· 3088 3067 flags |= (FIEMAP_EXTENT_DELALLOC | 3089 3068 FIEMAP_EXTENT_UNKNOWN); 3090 3069 } else { 3091 - disko = em->block_start; 3070 + disko = em->block_start + offset_in_extent; 3092 3071 } 3093 3072 if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) 3094 3073 flags |= FIEMAP_EXTENT_ENCODED;