|
@@ -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)
|
|
|
{
|