generic block based fiemap implementation
authorJosef Bacik <jbacik@redhat.com>
Fri, 3 Oct 2008 21:32:43 +0000 (17:32 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Fri, 3 Oct 2008 21:32:43 +0000 (17:32 -0400)
Any block based fs (this patch includes ext3) just has to declare its own
fiemap() function and then call this generic function with its own
get_block_t. This works well for block based filesystems that will map
multiple contiguous blocks at one time, but will work for filesystems that
only map one block at a time, you will just end up with an "extent" for each
block. One gotcha is this will not play nicely where there is hole+data
after the EOF. This function will assume its hit the end of the data as soon
as it hits a hole after the EOF, so if there is any data past that it will
not pick that up. AFAIK no block based fs does this anyway, but its in the
comments of the function anyway just in case.

Signed-off-by: Josef Bacik <jbacik@redhat.com>
Signed-off-by: Mark Fasheh <mfasheh@suse.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Cc: linux-fsdevel@vger.kernel.org
fs/ext2/ext2.h
fs/ext2/file.c
fs/ext2/inode.c
fs/ext3/file.c
fs/ext3/inode.c
fs/ioctl.c
include/linux/ext3_fs.h
include/linux/fs.h

index 47d88da2d33b5fab73349fa26776f7773d98d194..bae998c1e44eea3afef4a2e834d75d08ca5c472e 100644 (file)
@@ -133,6 +133,8 @@ extern void ext2_truncate (struct inode *);
 extern int ext2_setattr (struct dentry *, struct iattr *);
 extern void ext2_set_inode_flags(struct inode *inode);
 extern void ext2_get_inode_flags(struct ext2_inode_info *);
+extern int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+                      u64 start, u64 len);
 int __ext2_write_begin(struct file *file, struct address_space *mapping,
                loff_t pos, unsigned len, unsigned flags,
                struct page **pagep, void **fsdata);
index 5f2fa9c36293d835722ec1371b820b07d5c5d9b5..45ed071221821bac70f2d92d817cf24338cb4ccc 100644 (file)
@@ -86,4 +86,5 @@ const struct inode_operations ext2_file_inode_operations = {
 #endif
        .setattr        = ext2_setattr,
        .permission     = ext2_permission,
+       .fiemap         = ext2_fiemap,
 };
index 991d6dfeb51f078bfe746e6dede400e7e2525817..7658b33e2653c5da7eb0c00c8fc7dc12313f7745 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/writeback.h>
 #include <linux/buffer_head.h>
 #include <linux/mpage.h>
+#include <linux/fiemap.h>
 #include "ext2.h"
 #include "acl.h"
 #include "xip.h"
@@ -704,6 +705,13 @@ int ext2_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_
 
 }
 
+int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+               u64 start, u64 len)
+{
+       return generic_block_fiemap(inode, fieinfo, start, len,
+                                   ext2_get_block);
+}
+
 static int ext2_writepage(struct page *page, struct writeback_control *wbc)
 {
        return block_write_full_page(page, ext2_get_block, wbc);
index acc4913d30199079007726e8c4a49b4a07dd77cc..3be1e0689c9aa34443b5695f7800c41fbcafceef 100644 (file)
@@ -134,5 +134,6 @@ const struct inode_operations ext3_file_inode_operations = {
        .removexattr    = generic_removexattr,
 #endif
        .permission     = ext3_permission,
+       .fiemap         = ext3_fiemap,
 };
 
index 507d8689b111662b403c874e2c269417635f5555..ebfec4d0148e9e272e2a25efd0e3b551381408c6 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/mpage.h>
 #include <linux/uio.h>
 #include <linux/bio.h>
+#include <linux/fiemap.h>
 #include "xattr.h"
 #include "acl.h"
 
@@ -981,6 +982,13 @@ out:
        return ret;
 }
 
+int ext3_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+               u64 start, u64 len)
+{
+       return generic_block_fiemap(inode, fieinfo, start, len,
+                                   ext3_get_block);
+}
+
 /*
  * `handle' can be NULL if create is zero
  */
index 045d9601fbbd981f970b3fb0e0ff5914bc9afc1f..33a6b7ecb8b8686449fb42b1ab5f8ef6ce64709f 100644 (file)
@@ -13,6 +13,8 @@
 #include <linux/security.h>
 #include <linux/module.h>
 #include <linux/uaccess.h>
+#include <linux/writeback.h>
+#include <linux/buffer_head.h>
 
 #include <asm/ioctls.h>
 
@@ -224,6 +226,122 @@ static int ioctl_fiemap(struct file *filp, unsigned long arg)
        return error;
 }
 
+#define blk_to_logical(inode, blk) (blk << (inode)->i_blkbits)
+#define logical_to_blk(inode, offset) (offset >> (inode)->i_blkbits);
+
+/*
+ * @inode - the inode to map
+ * @arg - the pointer to userspace where we copy everything to
+ * @get_block - the fs's get_block function
+ *
+ * This does FIEMAP for block based inodes.  Basically it will just loop
+ * through get_block until we hit the number of extents we want to map, or we
+ * go past the end of the file and hit a hole.
+ *
+ * If it is possible to have data blocks beyond a hole past @inode->i_size, then
+ * please do not use this function, it will stop at the first unmapped block
+ * beyond i_size
+ */
+int generic_block_fiemap(struct inode *inode,
+                        struct fiemap_extent_info *fieinfo, u64 start,
+                        u64 len, get_block_t *get_block)
+{
+       struct buffer_head tmp;
+       unsigned int start_blk;
+       long long length = 0, map_len = 0;
+       u64 logical = 0, phys = 0, size = 0;
+       u32 flags = FIEMAP_EXTENT_MERGED;
+       int ret = 0;
+
+       if ((ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC)))
+               return ret;
+
+       start_blk = logical_to_blk(inode, start);
+
+       /* guard against change */
+       mutex_lock(&inode->i_mutex);
+
+       length = (long long)min_t(u64, len, i_size_read(inode));
+       map_len = length;
+
+       do {
+               /*
+                * we set b_size to the total size we want so it will map as
+                * many contiguous blocks as possible at once
+                */
+               memset(&tmp, 0, sizeof(struct buffer_head));
+               tmp.b_size = map_len;
+
+               ret = get_block(inode, start_blk, &tmp, 0);
+               if (ret)
+                       break;
+
+               /* HOLE */
+               if (!buffer_mapped(&tmp)) {
+                       /*
+                        * first hole after going past the EOF, this is our
+                        * last extent
+                        */
+                       if (length <= 0) {
+                               flags = FIEMAP_EXTENT_MERGED|FIEMAP_EXTENT_LAST;
+                               ret = fiemap_fill_next_extent(fieinfo, logical,
+                                                             phys, size,
+                                                             flags);
+                               break;
+                       }
+
+                       length -= blk_to_logical(inode, 1);
+
+                       /* if we have holes up to/past EOF then we're done */
+                       if (length <= 0)
+                               break;
+
+                       start_blk++;
+               } else {
+                       if (length <= 0 && size) {
+                               ret = fiemap_fill_next_extent(fieinfo, logical,
+                                                             phys, size,
+                                                             flags);
+                               if (ret)
+                                       break;
+                       }
+
+                       logical = blk_to_logical(inode, start_blk);
+                       phys = blk_to_logical(inode, tmp.b_blocknr);
+                       size = tmp.b_size;
+                       flags = FIEMAP_EXTENT_MERGED;
+
+                       length -= tmp.b_size;
+                       start_blk += logical_to_blk(inode, size);
+
+                       /*
+                        * if we are past the EOF we need to loop again to see
+                        * if there is a hole so we can mark this extent as the
+                        * last one, and if not keep mapping things until we
+                        * find a hole, or we run out of slots in the extent
+                        * array
+                        */
+                       if (length <= 0)
+                               continue;
+
+                       ret = fiemap_fill_next_extent(fieinfo, logical, phys,
+                                                     size, flags);
+                       if (ret)
+                               break;
+               }
+               cond_resched();
+       } while (1);
+
+       mutex_unlock(&inode->i_mutex);
+
+       /* if ret is 1 then we just hit the end of the extent array */
+       if (ret == 1)
+               ret = 0;
+
+       return ret;
+}
+EXPORT_SYMBOL(generic_block_fiemap);
+
 static int file_ioctl(struct file *filp, unsigned int cmd,
                unsigned long arg)
 {
index 80171ee89a2222b8e00fb65b2220b4f0fcf78165..8120fa1bc2357008f63c06c78d3304c781bf9341 100644 (file)
@@ -837,6 +837,8 @@ extern void ext3_truncate (struct inode *);
 extern void ext3_set_inode_flags(struct inode *);
 extern void ext3_get_inode_flags(struct ext3_inode_info *);
 extern void ext3_set_aops(struct inode *inode);
+extern int ext3_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+                      u64 start, u64 len);
 
 /* ioctl.c */
 extern int ext3_ioctl (struct inode *, struct file *, unsigned int,
index 194fb237a3072e5e9df4fa0b79736da2735d38e1..385c9a197df1a93c684235037d0097d938caaa56 100644 (file)
@@ -1998,6 +1998,9 @@ extern int vfs_fstat(unsigned int, struct kstat *);
 
 extern int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd,
                    unsigned long arg);
+extern int generic_block_fiemap(struct inode *inode,
+                               struct fiemap_extent_info *fieinfo, u64 start,
+                               u64 len, get_block_t *get_block);
 
 extern void get_filesystem(struct file_system_type *fs);
 extern void put_filesystem(struct file_system_type *fs);