xfs
[Top] [All Lists]

[PATCH 1/3] fs: Add support IOC_MOV_DATA ioctl

To: Dave Chinner <david@xxxxxxxxxxxxx>, Theodore Ts'o <tytso@xxxxxxx>
Subject: [PATCH 1/3] fs: Add support IOC_MOV_DATA ioctl
From: Namjae Jeon <namjae.jeon@xxxxxxxxxxx>
Date: Tue, 08 Jul 2014 20:59:37 +0900
Cc: linux-ext4 <linux-ext4@xxxxxxxxxxxxxxx>, linux-fsdevel@xxxxxxxxxxxxxxx, linux-kernel@xxxxxxxxxxxxxxx, Lukáš Czerner <lczerner@xxxxxxxxxx>, Brian Foster <bfoster@xxxxxxxxxx>, Christoph Hellwig <hch@xxxxxxxxxxxxx>, Ashish Sangwan <a.sangwan@xxxxxxxxxxx>, xfs@xxxxxxxxxxx
Delivered-to: xfs@xxxxxxxxxxx
Dlp-filter: Pass
Thread-index: Ac+aosMnVzxFR0gxRXO+9qFSHQEpIA==
For speeding non linear media editing operations, we have already implemeted
FALLOC_FL_COLLAPSE_RANGE (merged in kernel since 3.15) and 
FALLOC_FL_INSERT_RANGE (currently awaiting review).
Both of these fallocate flags are used to remove/insert data within same file.
In continuation of our effort of speeding non linear media editing
(although the use case is not limited to just media editing) we introduce here
an ioctl FS_IOC_MOV_DATA which moves arbitrary (but fs block size aligned
as of now) bytes of data from one file into other file .

The movement takes place by transfering complete extents from donor file to
receiver file and leaves a hole in the donor file at the point from where
the blocks are moved. To eliminate the hole from donor, user can call
COLLAPSE_RANGE after the ioctl is finished if contiguous file space is required.

The main data structure for this ioctl is:
struct mov_data {
       int     donor_fd;               /* fd of donor file */
       int     receiver_fd;            /* fd of receiver file */
       loff_t  donor_offset;           /* offset into donor file */
       loff_t  receiver_offset;        /* offset into receiver file */
       loff_t  length;                 /* data length to be moved */
       loff_t  moved_len;              /* data length actually moved after 
completion */
       int     flags;                  /* Currently unused */
};

FS_IOC_MOV_DATA will move length bytes of data from donor_fd's donor_offset
to receiver_fd's receiver_offset. The prerequisite is that there must be 
atleast length size hole present @receiver_offset.

For inserting hole within file size at receiver_offset, FALLOC_FL_INSERT_RANGE
can be used. We will shortly post new version of FALLOC_FL_INSERT_RANGE which
enables inserting hole instead of current behavior of allocating unwritten
extents. If the requirement is to create hole at the end of file, truncate(2)
will suffice.

Signed-off-by: Namjae Jeon <namjae.jeon@xxxxxxxxxxx>
Signed-off-by: Ashish Sangwan <a.sangwan@xxxxxxxxxxx>
---
 fs/ioctl.c              | 108 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fs.h      |   2 +
 include/uapi/linux/fs.h |  13 ++++++
 3 files changed, 123 insertions(+)

diff --git a/fs/ioctl.c b/fs/ioctl.c
index 8ac3fad..a1508f8 100644
--- a/fs/ioctl.c
+++ b/fs/ioctl.c
@@ -215,6 +215,111 @@ static int ioctl_fiemap(struct file *filp, unsigned long 
arg)
        return error;
 }
 
+static int ioctl_mov_data(struct file *filp, unsigned long arg)
+{
+       int error;
+       struct mov_data m_data;
+       struct mov_data __user *um_data = (struct mov_data __user *) arg;
+       struct inode *donor, *receiver;
+       struct fd donor_fd, receiver_fd;
+
+       if (copy_from_user(&m_data, um_data, sizeof(struct mov_data)))
+               return -EFAULT;
+
+       if (m_data.donor_offset < 0 || m_data.receiver_offset < 0 ||
+           m_data.length <= 0)
+               return -EINVAL;
+
+       donor_fd = fdget(m_data.donor_fd);
+       if (!donor_fd.file) {
+               error = -EINVAL;
+               goto out1;
+       }
+
+       if (!(donor_fd.file->f_mode & FMODE_WRITE) ||
+           !(donor_fd.file->f_mode & FMODE_READ) ||
+           (donor_fd.file->f_flags & O_APPEND)) {
+               error = -EBADF;
+               goto out1;
+       }
+
+       receiver_fd = fdget(m_data.receiver_fd);
+       if (!receiver_fd.file) {
+               error = -EINVAL;
+               goto out2;
+       }
+
+       if (!(receiver_fd.file->f_mode & FMODE_WRITE) ||
+           !(receiver_fd.file->f_mode & FMODE_READ) ||
+           (receiver_fd.file->f_flags & O_APPEND)) {
+               error = -EBADF;
+               goto out2;
+       }
+
+       donor = file_inode(donor_fd.file);
+       receiver = file_inode(receiver_fd.file);
+
+       if (donor->i_sb != receiver->i_sb) {
+               error = -EINVAL;
+               goto out2;
+       }
+
+       if (donor == receiver) {
+               error = -EINVAL;
+               goto out2;
+       }
+
+       error = security_file_permission(donor_fd.file, MAY_WRITE);
+       if (error)
+               goto out2;
+
+       error = security_file_permission(receiver_fd.file, MAY_WRITE);
+       if (error)
+               goto out2;
+
+       if (IS_IMMUTABLE(donor) || IS_IMMUTABLE(receiver)) {
+               error = -EPERM;
+               goto out2;
+       }
+
+       if (IS_SWAPFILE(donor) || IS_SWAPFILE(receiver)) {
+               error = -EINVAL;
+               goto out2;
+       }
+
+       if (S_ISFIFO(donor->i_mode) | S_ISFIFO(receiver->i_mode)) {
+               error = -ESPIPE;
+               goto out2;
+       }
+
+       if (!S_ISREG(donor->i_mode) || !S_ISREG(receiver->i_mode)) {
+               error = -ENODEV;
+               goto out2;
+       }
+
+       if (!donor->i_op->mov_data) {
+               error = -EOPNOTSUPP;
+               goto out2;
+       }
+
+       m_data.moved_len = 0;
+       sb_start_write(donor->i_sb);
+       error = donor->i_op->mov_data(donor, receiver, m_data.donor_offset,
+                                     m_data.receiver_offset, m_data.length,
+                                     &m_data.moved_len);
+       sb_end_write(donor->i_sb);
+
+       if (copy_to_user(um_data, &m_data, sizeof(struct mov_data)))
+               error = -EFAULT;
+
+out2:
+       fdput(receiver_fd);
+out1:
+       fdput(donor_fd);
+
+       return error;
+}
+
 #ifdef CONFIG_BLOCK
 
 static inline sector_t logical_to_blk(struct inode *inode, loff_t offset)
@@ -591,6 +696,9 @@ int do_vfs_ioctl(struct file *filp, unsigned int fd, 
unsigned int cmd,
        case FIGETBSZ:
                return put_user(inode->i_sb->s_blocksize, argp);
 
+       case FS_IOC_MOV_DATA:
+               return ioctl_mov_data(filp, arg);
+
        default:
                if (S_ISREG(inode->i_mode))
                        error = file_ioctl(filp, cmd, arg);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 1649d9d..a13afe3 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1520,6 +1520,8 @@ struct inode_operations {
                           umode_t create_mode, int *opened);
        int (*tmpfile) (struct inode *, struct dentry *, umode_t);
        int (*set_acl)(struct inode *, struct posix_acl *, int);
+       int (*mov_data)(struct inode *, struct inode *, loff_t, loff_t,
+                       loff_t, loff_t *);
 } ____cacheline_aligned;
 
 ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index ca1a11b..6766d12 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -57,6 +57,18 @@ struct inodes_stat_t {
        long dummy[5];          /* padding for sysctl ABI compatibility */
 };
 
+/*
+ * Structure passed to IOC_MOV_DATA
+ */
+struct mov_data {
+       int     donor_fd;               /* fd of donor file */
+       int     receiver_fd;            /* fd of receiver file */
+       loff_t  donor_offset;           /* offset into donor file */
+       loff_t  receiver_offset;        /* offset into receiver file */
+       loff_t  length;                 /* data length to be moved */
+       loff_t  moved_len;              /* successfully moved length */
+       int     flags;                  /* option to expand new feature */
+};
 
 #define NR_FILE  8192  /* this can well be larger on a larger system */
 
@@ -162,6 +174,7 @@ struct inodes_stat_t {
 #define        FS_IOC_GETVERSION               _IOR('v', 1, long)
 #define        FS_IOC_SETVERSION               _IOW('v', 2, long)
 #define FS_IOC_FIEMAP                  _IOWR('f', 11, struct fiemap)
+#define FS_IOC_MOV_DATA                        _IOWR('f', 12, struct mov_data)
 #define FS_IOC32_GETFLAGS              _IOR('f', 1, int)
 #define FS_IOC32_SETFLAGS              _IOW('f', 2, int)
 #define FS_IOC32_GETVERSION            _IOR('v', 1, int)
-- 
1.7.11-rc0

<Prev in Thread] Current Thread [Next in Thread>
  • [PATCH 1/3] fs: Add support IOC_MOV_DATA ioctl, Namjae Jeon <=