• BingJing Chang's avatar
    btrfs: fix a potential hole punching failure · 3227788c
    BingJing Chang authored
    In commit d7781546 ("btrfs: Avoid trucating page or punching hole
    in a already existed hole."), existing holes can be skipped by calling
    find_first_non_hole() to adjust start and len. However, if the given len
    is invalid and large, when an EXTENT_MAP_HOLE extent is found, len will
    not be set to zero because (em->start + em->len) is less than
    (start + len). Then the ret will be 1 but len will not be set to 0.
    The propagated non-zero ret will result in fallocate failure.
    
    In the while-loop of btrfs_replace_file_extents(), len is not updated
    every time before it calls find_first_non_hole(). That is, after
    btrfs_drop_extents() successfully drops the last non-hole file extent,
    it may fail with ENOSPC when attempting to drop a file extent item
    representing a hole. The problem can happen. After it calls
    find_first_non_hole(), the cur_offset will be adjusted to be larger
    than or equal to end. However, since the len is not set to zero, the
    break-loop condition (ret && !len) will not be met. After it leaves the
    while-loop, fallocate will return 1, which is an unexpected return
    value.
    
    We're not able to construct a reproducible way to let
    btrfs_drop_extents() fail with ENOSPC after it drops the last non-hole
    file extent but with remaining holes left. However, it's quite easy to
    fix. We just need to update and check the len every time before we call
    find_first_non_hole(). To make the while loop more readable, we also
    pull the variable updates to the bottom of loop like this:
      while (cur_offset < end) {
    	  ...
    	  // update cur_offset & len
    	  // advance cur_offset & len in hole-punching case if needed
      }
    Reported-by: default avatarRobbie Ko <robbieko@synology.com>
    Fixes: d7781546 ("btrfs: Avoid trucating page or punching hole in a already existed hole.")
    CC: stable@vger.kernel.org # 4.4+
    Reviewed-by: default avatarRobbie Ko <robbieko@synology.com>
    Reviewed-by: default avatarChung-Chiang Cheng <cccheng@synology.com>
    Reviewed-by: default avatarFilipe Manana <fdmanana@suse.com>
    Signed-off-by: default avatarBingJing Chang <bingjingc@synology.com>
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    3227788c
file.c 98.3 KB