@@ -20,6 +20,8 @@
#include "ext4_extents.h"
#include "group.h"
+#define FIEMAP_MAX_EXTENTS (UINT_MAX / sizeof(struct fiemap_extent))
+
/**
* ext4_defrag_next_extent - Search for the next extent and set it to "extent"
*
@@ -91,6 +93,106 @@ err:
}
/**
+ * ext4_defrag_fiemap_ino - Get extents information by inode number
+ *
+ * @filp: pointer to file
+ * @arg: pointer to fiemap_ino
+ * @fiemap_ino->ino: an inode number which is used to get
+ * extent information
+ * @fiemap_ino->fiemap: request for fiemap ioctl
+ *
+ * This function returns 0 if succeed, otherwise returns error value.
+ */
+static int
+ext4_defrag_fiemap_ino(struct file *filp, unsigned long arg)
+{
+ struct fiemap_ino fiemap_ino;
+ struct fiemap_extent_info fieinfo = { 0, };
+ struct inode *inode;
+ struct super_block *sb = filp->f_dentry->d_inode->i_sb;
+ u64 len = 0;
+ int err = 0;
+
+ if (copy_from_user(&fiemap_ino, (struct fiemap_ino __user *)arg,
+ sizeof(struct fiemap_ino)))
+ return -EFAULT;
+
+ /* Special inodes shouldn't be choiced */
+ if (fiemap_ino.ino < EXT4_GOOD_OLD_FIRST_INO)
+ return -ENOENT;
+
+ inode = ext4_iget(sb, fiemap_ino.ino);
+ if (IS_ERR(inode))
+ return PTR_ERR(inode);
+
+ /* Return -ENOENT if a file does not exist */
+ if (!inode->i_nlink || !S_ISREG(inode->i_mode)) {
+ err = -ENOENT;
+ goto out;
+ }
+
+ if (!inode->i_op->fiemap) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (fiemap_ino.fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (fiemap_ino.fiemap.fm_length == 0) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (fiemap_ino.fiemap.fm_start > sb->s_maxbytes) {
+ err = -EFBIG;
+ goto out;
+ }
+
+ /*
+ * Check offset and length
+ * If the specified range exceeds the max file size,
+ * adjust the length.
+ */
+ if ((fiemap_ino.fiemap.fm_length > sb->s_maxbytes) ||
+ (sb->s_maxbytes - fiemap_ino.fiemap.fm_length)
+ < fiemap_ino.fiemap.fm_start)
+ len = sb->s_maxbytes - fiemap_ino.fiemap.fm_start;
+ else
+ len = fiemap_ino.fiemap.fm_length;
+
+ fieinfo.fi_flags = fiemap_ino.fiemap.fm_flags;
+ fieinfo.fi_extents_max = fiemap_ino.fiemap.fm_extent_count;
+ fieinfo.fi_extents_start =
+ (struct fiemap_extent *)(arg + sizeof(fiemap_ino));
+
+ if (fiemap_ino.fiemap.fm_extent_count != 0 &&
+ !access_ok(VERIFY_WRITE, fieinfo.fi_extents_start,
+ fieinfo.fi_extents_max * sizeof(struct fiemap_extent))) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC)
+ filemap_write_and_wait(inode->i_mapping);
+
+ err = inode->i_op->fiemap(inode, &fieinfo,
+ fiemap_ino.fiemap.fm_start, len);
+ if (!err) {
+ fiemap_ino.fiemap.fm_flags = fieinfo.fi_flags;
+ fiemap_ino.fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;
+ if (copy_to_user((char *)arg, &fiemap_ino, sizeof(fiemap_ino)))
+ err = -EFAULT;
+ }
+
+out:
+ iput(inode);
+ return err;
+}
+
+/**
* ext4_defrag_fblocks_distribution - Search free blocks distribution
*
* @org_inode: original inode
@@ -237,6 +339,9 @@ int ext4_defrag_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
err = copy_to_user(
(struct ext4_extents_info __user *)arg,
&ext_info, sizeof(ext_info));
+ } else if (cmd == EXT4_IOC_FIEMAP_INO) {
+
+ err = ext4_defrag_fiemap_ino(filp, arg);
} else if (cmd == EXT4_IOC_DEFRAG) {
struct ext4_ext_defrag_data defrag;
struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
@@ -306,6 +306,7 @@ struct ext4_new_group_data {
#define EXT4_IOC_FIBMAP _IOW('f', 16, ext4_fsblk_t)
#define EXT4_IOC_GROUP_INFO _IOW('f', 17, struct ext4_group_data_info)
#define EXT4_IOC_FREE_BLOCKS_INFO _IOW('f', 18, struct ext4_extents_info)
+#define EXT4_IOC_FIEMAP_INO _IOW('f', 19, struct fiemap_ino)
/*
* ioctl commands in 32 bit emulation
@@ -358,6 +359,11 @@ struct ext4_extents_info {
struct ext4_extent_data ext[DEFRAG_MAX_ENT];
};
+struct fiemap_ino {
+ __u64 ino;
+ struct fiemap fiemap;
+};
+
#define EXT4_TRANS_META_BLOCKS 4 /* bitmap + group desc + sb + inode */
/*
@@ -1321,6 +1327,7 @@ extern int ext4_get_blocks_wrap(handle_t *handle, struct inode *inode,
sector_t block, unsigned long max_blocks,
struct buffer_head *bh, int create,
int extend_disksize, int flag);
+extern int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start);
#endif /* __KERNEL__ */
#endif /* _EXT4_H */
@@ -2110,7 +2110,7 @@ ext4_ext_more_to_rm(struct ext4_ext_path *path)
return 1;
}
-static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
+int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
{
struct super_block *sb = inode->i_sb;
int depth = ext_depth(inode);
@@ -259,7 +259,8 @@ setversion_out:
case EXT4_IOC_FIBMAP:
case EXT4_IOC_DEFRAG:
case EXT4_IOC_GROUP_INFO:
- case EXT4_IOC_FREE_BLOCKS_INFO: {
+ case EXT4_IOC_FREE_BLOCKS_INFO:
+ case EXT4_IOC_FIEMAP_INO: {
return ext4_defrag_ioctl(inode, filp, cmd, arg);
}
case EXT4_IOC_GROUP_ADD: {