From: Eric Sandeen <sandeen@xxxxxxxxxx> Hook ext4 to the vfs fiemap interface. Signed-off-by: Eric Sandeen <sandeen@xxxxxxxxxx> --- fs/ext4/ext4.h | 2 + fs/ext4/ext4_extents.h | 2 +- fs/ext4/extents.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++- fs/ext4/file.c | 4 ++ 4 files changed, 126 insertions(+), 2 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 8158083..55f7ca1 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -383,6 +383,8 @@ struct ext4_inode { __le32 i_version_hi; /* high 32 bits for 64-bit version */ }; +#define EXT4_FIEMAP_FLAG_INCOMPAT_UNSUPP (FIEMAP_FLAG_INCOMPAT & \ + ~(FIEMAP_FLAG_LUN_OFFSET)) #define EXT4_EPOCH_BITS 2 #define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1) diff --git a/fs/ext4/ext4_extents.h b/fs/ext4/ext4_extents.h index 20906b7..c9fabb0 100644 --- a/fs/ext4/ext4_extents.h +++ b/fs/ext4/ext4_extents.h @@ -132,7 +132,7 @@ struct ext4_ext_path { */ typedef int (*ext_prepare_callback)(struct inode *, struct ext4_ext_path *, struct ext4_ext_cache *, - void *); + struct ext4_extent *, void *); #define EXT_CONTINUE 0 #define EXT_BREAK 1 diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index ea8dd06..fe7687c 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -40,6 +40,7 @@ #include <linux/slab.h> #include <linux/falloc.h> #include <asm/uaccess.h> +#include <linux/fiemap.h> #include "ext4_jbd2.h" #include "ext4_extents.h" @@ -1664,11 +1665,12 @@ int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block, } BUG_ON(cbex.ec_len == 0); - err = func(inode, path, &cbex, cbdata); + err = func(inode, path, &cbex, ex, cbdata); ext4_ext_drop_refs(path); if (err < 0) break; + if (err == EXT_REPEAT) continue; else if (err == EXT_BREAK) { @@ -3051,3 +3053,119 @@ retry: mutex_unlock(&inode->i_mutex); return ret > 0 ? ret2 : ret; } + +struct fiemap_internal { + struct fiemap *fiemap_s; + struct fiemap_extent fm_extent; + char *cur_ext_ptr; + int err; +}; + +/* + * Callback function called for each extent to gather FIEMAP information. + */ +int ext4_ext_fiemap_cb(struct inode *inode, struct ext4_ext_path *path, + struct ext4_ext_cache *newex, struct ext4_extent *ex, + void *data) +{ + struct fiemap_extent_info *fieinfo = data; + unsigned long blksize_bits = inode->i_sb->s_blocksize_bits; + __u64 logical; + __u64 physical; + __u64 length; + __u32 flags = 0; + int error; + + logical = (__u64)newex->ec_block << blksize_bits; + + if (newex->ec_type == EXT4_EXT_CACHE_GAP) { + pgoff_t offset; + struct page *page; + struct buffer_head *bh; + + offset = logical >> PAGE_SHIFT; + page = find_get_page(inode->i_mapping, offset); + if (!page) + return EXT_CONTINUE; + + bh = page_buffers(page); + + if (!bh) + return EXT_CONTINUE; + + if (buffer_delay(bh)) { + flags |= FIEMAP_EXTENT_DELALLOC; + page_cache_release(page); + } else { + page_cache_release(page); + return EXT_CONTINUE; + } + } + + physical = (__u64)newex->ec_start << blksize_bits; + length = (__u64)newex->ec_len << blksize_bits; + + /* Just counting extents? */ + if (fieinfo->fi_flags & FIEMAP_FLAG_NUM_EXTENTS) { + fieinfo->fi_extents_mapped++; + return EXT_CONTINUE; + } + + if (ex && ext4_ext_is_uninitialized(ex)) + flags |= FIEMAP_EXTENT_UNWRITTEN; + + /* + * If this extent reaches EXT_MAX_BLOCK, it must be last. + * + * Or if ext4_ext_next_allocated_block is EXT_MAX_BLOCK, + * this indicates no more allocated blocks. + * + * XXX this might miss a single-block extent at EXT_MAX_BLOCK + */ + if (logical + length - 1 == EXT_MAX_BLOCK || + ext4_ext_next_allocated_block(path) == EXT_MAX_BLOCK) + flags |= FIEMAP_EXTENT_LAST; + + error = fiemap_fill_next_extent(fieinfo, logical, physical, + length, flags, 0); + if (error < 0) + return error; + if (error == 1) + return EXT_BREAK; + + return EXT_CONTINUE; +} + +int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + __u64 start, __u64 len) +{ + ext4_lblk_t start_blk; + ext4_lblk_t len_blks; + int err; + + /* fallback to generic here if not extents */ +#warning fix bitmap fallback + if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)) + return -EOPNOTSUPP; + +#if 0 + /* bail on unsupported flags for this fs */ + if (fiemap_s->fm_flags & EXT4_FIEMAP_FLAG_INCOMPAT_UNSUPP) + return -EOPNOTSUPP; +#endif + + start_blk = start >> inode->i_sb->s_blocksize_bits; + len_blks = len >> inode->i_sb->s_blocksize_bits; + + /* + * Walk the extent tree gathering extent information. + * ext4_ext_fiemap_cb will push extents back to user. + */ + down_write(&EXT4_I(inode)->i_data_sem); + err = ext4_ext_walk_space(inode, start_blk, len_blks, + ext4_ext_fiemap_cb, fieinfo); + up_write(&EXT4_I(inode)->i_data_sem); + + return err; +} + diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 4159be6..99bde24 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -123,6 +123,9 @@ force_commit: return ret; } +extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + __u64 start, __u64 len); + const struct file_operations ext4_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read, @@ -152,5 +155,6 @@ const struct inode_operations ext4_file_inode_operations = { #endif .permission = ext4_permission, .fallocate = ext4_fallocate, + .fiemap = ext4_fiemap, }; -- 1.5.4.1 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html