#include <linux/gfs2_ondisk.h>
 #include <linux/crc32.h>
 #include <linux/lm_interface.h>
+#include <linux/fiemap.h>
 #include <asm/uaccess.h>
 
 #include "gfs2.h"
        return gfs2_ea_remove(GFS2_I(dentry->d_inode), &er);
 }
 
+static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
+                      u64 start, u64 len)
+{
+       struct gfs2_inode *ip = GFS2_I(inode);
+       struct gfs2_holder gh;
+       int ret;
+
+       ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC);
+       if (ret)
+               return ret;
+
+       mutex_lock(&inode->i_mutex);
+
+       ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
+       if (ret)
+               goto out;
+
+       if (gfs2_is_stuffed(ip)) {
+               u64 phys = ip->i_no_addr << inode->i_blkbits;
+               u64 size = i_size_read(inode);
+               u32 flags = FIEMAP_EXTENT_LAST|FIEMAP_EXTENT_NOT_ALIGNED|
+                           FIEMAP_EXTENT_DATA_INLINE;
+               phys += sizeof(struct gfs2_dinode);
+               phys += start;
+               if (start + len > size)
+                       len = size - start;
+               if (start < size)
+                       ret = fiemap_fill_next_extent(fieinfo, start, phys,
+                                                     len, flags);
+               if (ret == 1)
+                       ret = 0;
+       } else {
+               ret = __generic_block_fiemap(inode, fieinfo, start, len,
+                                            gfs2_block_map);
+       }
+
+       gfs2_glock_dq_uninit(&gh);
+out:
+       mutex_unlock(&inode->i_mutex);
+       return ret;
+}
+
 const struct inode_operations gfs2_file_iops = {
        .permission = gfs2_permission,
        .setattr = gfs2_setattr,
        .getxattr = gfs2_getxattr,
        .listxattr = gfs2_listxattr,
        .removexattr = gfs2_removexattr,
+       .fiemap = gfs2_fiemap,
 };
 
 const struct inode_operations gfs2_dir_iops = {
        .getxattr = gfs2_getxattr,
        .listxattr = gfs2_listxattr,
        .removexattr = gfs2_removexattr,
+       .fiemap = gfs2_fiemap,
 };
 
 const struct inode_operations gfs2_symlink_iops = {
        .getxattr = gfs2_getxattr,
        .listxattr = gfs2_listxattr,
        .removexattr = gfs2_removexattr,
+       .fiemap = gfs2_fiemap,
 };
 
 
 #define blk_to_logical(inode, blk) (blk << (inode)->i_blkbits)
 #define logical_to_blk(inode, offset) (offset >> (inode)->i_blkbits);
 
-/*
+/**
+ * __generic_block_fiemap - FIEMAP for block based inodes (no locking)
  * @inode - the inode to map
  * @arg - the pointer to userspace where we copy everything to
  * @get_block - the fs's get_block function
  *
  * 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
+ * beyond i_size.
+ *
+ * If you use this function directly, you need to do your own locking. Use
+ * generic_block_fiemap if you want the locking done for you.
  */
-int generic_block_fiemap(struct inode *inode,
-                        struct fiemap_extent_info *fieinfo, u64 start,
-                        u64 len, get_block_t *get_block)
+
+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;
 
        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;
 
                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);
+
+/**
+ * generic_block_fiemap - FIEMAP for block based inodes
+ * @inode: The inode to map
+ * @fieinfo: The mapping information
+ * @start: The initial block to map
+ * @len: The length of the extect to attempt to map
+ * @get_block: The block mapping function for the fs
+ *
+ * Calls __generic_block_fiemap to map the inode, after taking
+ * the inode's mutex lock.
+ */
+
+int generic_block_fiemap(struct inode *inode,
+                        struct fiemap_extent_info *fieinfo, u64 start,
+                        u64 len, get_block_t *get_block)
+{
+       int ret;
+       mutex_lock(&inode->i_mutex);
+       ret = __generic_block_fiemap(inode, fieinfo, start, len, get_block);
+       mutex_unlock(&inode->i_mutex);
+       return ret;
+}
 EXPORT_SYMBOL(generic_block_fiemap);
 
 #endif  /*  CONFIG_BLOCK  */
 
 
 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 int generic_block_fiemap(struct inode *inode,
                                struct fiemap_extent_info *fieinfo, u64 start,
                                u64 len, get_block_t *get_block);