diff options
Diffstat (limited to 'fs/btrfs/extent_io.c')
| -rw-r--r-- | fs/btrfs/extent_io.c | 93 |
1 files changed, 92 insertions, 1 deletions
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index e086d407f1fa..a3b0676403f7 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | #include <linux/spinlock.h> | 9 | #include <linux/spinlock.h> |
| 10 | #include <linux/blkdev.h> | 10 | #include <linux/blkdev.h> |
| 11 | #include <linux/swap.h> | 11 | #include <linux/swap.h> |
| 12 | #include <linux/version.h> | ||
| 13 | #include <linux/writeback.h> | 12 | #include <linux/writeback.h> |
| 14 | #include <linux/pagevec.h> | 13 | #include <linux/pagevec.h> |
| 15 | #include "extent_io.h" | 14 | #include "extent_io.h" |
| @@ -2855,6 +2854,98 @@ out: | |||
| 2855 | return sector; | 2854 | return sector; |
| 2856 | } | 2855 | } |
| 2857 | 2856 | ||
| 2857 | int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, | ||
| 2858 | __u64 start, __u64 len, get_extent_t *get_extent) | ||
| 2859 | { | ||
| 2860 | int ret; | ||
| 2861 | u64 off = start; | ||
| 2862 | u64 max = start + len; | ||
| 2863 | u32 flags = 0; | ||
| 2864 | u64 disko = 0; | ||
| 2865 | struct extent_map *em = NULL; | ||
| 2866 | int end = 0; | ||
| 2867 | u64 em_start = 0, em_len = 0; | ||
| 2868 | unsigned long emflags; | ||
| 2869 | ret = 0; | ||
| 2870 | |||
| 2871 | if (len == 0) | ||
| 2872 | return -EINVAL; | ||
| 2873 | |||
| 2874 | lock_extent(&BTRFS_I(inode)->io_tree, start, start + len, | ||
| 2875 | GFP_NOFS); | ||
| 2876 | em = get_extent(inode, NULL, 0, off, max - off, 0); | ||
| 2877 | if (!em) | ||
| 2878 | goto out; | ||
| 2879 | if (IS_ERR(em)) { | ||
| 2880 | ret = PTR_ERR(em); | ||
| 2881 | goto out; | ||
| 2882 | } | ||
| 2883 | while (!end) { | ||
| 2884 | off = em->start + em->len; | ||
| 2885 | if (off >= max) | ||
| 2886 | end = 1; | ||
| 2887 | |||
| 2888 | em_start = em->start; | ||
| 2889 | em_len = em->len; | ||
| 2890 | |||
| 2891 | disko = 0; | ||
| 2892 | flags = 0; | ||
| 2893 | |||
| 2894 | switch (em->block_start) { | ||
| 2895 | case EXTENT_MAP_LAST_BYTE: | ||
| 2896 | end = 1; | ||
| 2897 | flags |= FIEMAP_EXTENT_LAST; | ||
| 2898 | break; | ||
| 2899 | case EXTENT_MAP_HOLE: | ||
| 2900 | flags |= FIEMAP_EXTENT_UNWRITTEN; | ||
| 2901 | break; | ||
| 2902 | case EXTENT_MAP_INLINE: | ||
| 2903 | flags |= (FIEMAP_EXTENT_DATA_INLINE | | ||
| 2904 | FIEMAP_EXTENT_NOT_ALIGNED); | ||
| 2905 | break; | ||
| 2906 | case EXTENT_MAP_DELALLOC: | ||
| 2907 | flags |= (FIEMAP_EXTENT_DELALLOC | | ||
| 2908 | FIEMAP_EXTENT_UNKNOWN); | ||
| 2909 | break; | ||
| 2910 | default: | ||
| 2911 | disko = em->block_start; | ||
| 2912 | break; | ||
| 2913 | } | ||
| 2914 | if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) | ||
| 2915 | flags |= FIEMAP_EXTENT_ENCODED; | ||
| 2916 | |||
| 2917 | emflags = em->flags; | ||
| 2918 | free_extent_map(em); | ||
| 2919 | em = NULL; | ||
| 2920 | |||
| 2921 | if (!end) { | ||
| 2922 | em = get_extent(inode, NULL, 0, off, max - off, 0); | ||
| 2923 | if (!em) | ||
| 2924 | goto out; | ||
| 2925 | if (IS_ERR(em)) { | ||
| 2926 | ret = PTR_ERR(em); | ||
| 2927 | goto out; | ||
| 2928 | } | ||
| 2929 | emflags = em->flags; | ||
| 2930 | } | ||
| 2931 | if (test_bit(EXTENT_FLAG_VACANCY, &emflags)) { | ||
| 2932 | flags |= FIEMAP_EXTENT_LAST; | ||
| 2933 | end = 1; | ||
| 2934 | } | ||
| 2935 | |||
| 2936 | ret = fiemap_fill_next_extent(fieinfo, em_start, disko, | ||
| 2937 | em_len, flags); | ||
| 2938 | if (ret) | ||
| 2939 | goto out_free; | ||
| 2940 | } | ||
| 2941 | out_free: | ||
| 2942 | free_extent_map(em); | ||
| 2943 | out: | ||
| 2944 | unlock_extent(&BTRFS_I(inode)->io_tree, start, start + len, | ||
| 2945 | GFP_NOFS); | ||
| 2946 | return ret; | ||
| 2947 | } | ||
| 2948 | |||
| 2858 | static inline struct page *extent_buffer_page(struct extent_buffer *eb, | 2949 | static inline struct page *extent_buffer_page(struct extent_buffer *eb, |
| 2859 | unsigned long i) | 2950 | unsigned long i) |
| 2860 | { | 2951 | { |
