Btrfs: fix fiemap

There are two big problems currently with FIEMAP

1) We return extents for holes. This isn't supposed to happen, we just don't
return extents for holes and then userspace interprets the lack of an extent as
a hole.

2) We sometimes don't set FIEMAP_EXTENT_LAST properly. This is because we wait
to see a EXTENT_FLAG_VACANCY flag on the em, but this won't happen if say we ask
fiemap to map up to the last extent in a file, and there is nothing but holes up
to the i_size. To fix this we need to lookup the last extent in this file and
save the logical offset, so if we happen to try and map that extent we can be
sure to set FIEMAP_EXTENT_LAST.

With this patch we now pass xfstest 225, which we never have before.

Signed-off-by: Josef Bacik <josef@redhat.com>
Signed-off-by: Chris Mason <chris.mason@oracle.com>

authored by Josef Bacik and committed by Chris Mason 975f84fe 619c8c76

+54 -9
+54 -9
fs/btrfs/extent_io.c
··· 2901 2901 int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, 2902 2902 __u64 start, __u64 len, get_extent_t *get_extent) 2903 2903 { 2904 - int ret; 2904 + int ret = 0; 2905 2905 u64 off = start; 2906 2906 u64 max = start + len; 2907 2907 u32 flags = 0; 2908 + u32 found_type; 2909 + u64 last; 2908 2910 u64 disko = 0; 2911 + struct btrfs_key found_key; 2909 2912 struct extent_map *em = NULL; 2910 2913 struct extent_state *cached_state = NULL; 2914 + struct btrfs_path *path; 2915 + struct btrfs_file_extent_item *item; 2911 2916 int end = 0; 2912 2917 u64 em_start = 0, em_len = 0; 2913 2918 unsigned long emflags; 2914 - ret = 0; 2919 + int hole = 0; 2915 2920 2916 2921 if (len == 0) 2917 2922 return -EINVAL; 2923 + 2924 + path = btrfs_alloc_path(); 2925 + if (!path) 2926 + return -ENOMEM; 2927 + path->leave_spinning = 1; 2928 + 2929 + ret = btrfs_lookup_file_extent(NULL, BTRFS_I(inode)->root, 2930 + path, inode->i_ino, -1, 0); 2931 + if (ret < 0) { 2932 + btrfs_free_path(path); 2933 + return ret; 2934 + } 2935 + WARN_ON(!ret); 2936 + path->slots[0]--; 2937 + item = btrfs_item_ptr(path->nodes[0], path->slots[0], 2938 + struct btrfs_file_extent_item); 2939 + btrfs_item_key_to_cpu(path->nodes[0], &found_key, path->slots[0]); 2940 + found_type = btrfs_key_type(&found_key); 2941 + 2942 + /* No extents, just return */ 2943 + if (found_key.objectid != inode->i_ino || 2944 + found_type != BTRFS_EXTENT_DATA_KEY) { 2945 + btrfs_free_path(path); 2946 + return 0; 2947 + } 2948 + last = found_key.offset; 2949 + btrfs_free_path(path); 2918 2950 2919 2951 lock_extent_bits(&BTRFS_I(inode)->io_tree, start, start + len, 0, 2920 2952 &cached_state, GFP_NOFS); ··· 2957 2925 ret = PTR_ERR(em); 2958 2926 goto out; 2959 2927 } 2928 + 2960 2929 while (!end) { 2930 + hole = 0; 2961 2931 off = em->start + em->len; 2962 2932 if (off >= max) 2963 2933 end = 1; 2934 + 2935 + if (em->block_start == EXTENT_MAP_HOLE) { 2936 + hole = 1; 2937 + goto next; 2938 + } 2964 2939 2965 2940 em_start = em->start; 2966 2941 em_len = em->len; ··· 2978 2939 if (em->block_start == EXTENT_MAP_LAST_BYTE) { 2979 2940 end = 1; 2980 2941 flags |= FIEMAP_EXTENT_LAST; 2981 - } else if (em->block_start == EXTENT_MAP_HOLE) { 2982 - flags |= FIEMAP_EXTENT_UNWRITTEN; 2983 2942 } else if (em->block_start == EXTENT_MAP_INLINE) { 2984 2943 flags |= (FIEMAP_EXTENT_DATA_INLINE | 2985 2944 FIEMAP_EXTENT_NOT_ALIGNED); ··· 2990 2953 if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) 2991 2954 flags |= FIEMAP_EXTENT_ENCODED; 2992 2955 2956 + next: 2993 2957 emflags = em->flags; 2994 2958 free_extent_map(em); 2995 2959 em = NULL; 2996 - 2997 2960 if (!end) { 2998 2961 em = get_extent(inode, NULL, 0, off, max - off, 0); 2999 2962 if (!em) ··· 3004 2967 } 3005 2968 emflags = em->flags; 3006 2969 } 2970 + 3007 2971 if (test_bit(EXTENT_FLAG_VACANCY, &emflags)) { 3008 2972 flags |= FIEMAP_EXTENT_LAST; 3009 2973 end = 1; 3010 2974 } 3011 2975 3012 - ret = fiemap_fill_next_extent(fieinfo, em_start, disko, 3013 - em_len, flags); 3014 - if (ret) 3015 - goto out_free; 2976 + if (em_start == last) { 2977 + flags |= FIEMAP_EXTENT_LAST; 2978 + end = 1; 2979 + } 2980 + 2981 + if (!hole) { 2982 + ret = fiemap_fill_next_extent(fieinfo, em_start, disko, 2983 + em_len, flags); 2984 + if (ret) 2985 + goto out_free; 2986 + } 3016 2987 } 3017 2988 out_free: 3018 2989 free_extent_map(em);