ovl: add reflink/copyfile/dedup support
authorMiklos Szeredi <mszeredi@redhat.com>
Wed, 18 Jul 2018 13:44:42 +0000 (15:44 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Wed, 18 Jul 2018 13:44:42 +0000 (15:44 +0200)
Since set of arguments are so similar, handle in a common helper.

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/overlayfs/file.c

index c49af241c0013b7872e5579f2366a15d2f2421a8..cd75b53f14976157718caa664cc2d92bd3169a12 100644 (file)
@@ -389,6 +389,89 @@ static long ovl_compat_ioctl(struct file *file, unsigned int cmd,
        return ovl_ioctl(file, cmd, arg);
 }
 
+enum ovl_copyop {
+       OVL_COPY,
+       OVL_CLONE,
+       OVL_DEDUPE,
+};
+
+static ssize_t ovl_copyfile(struct file *file_in, loff_t pos_in,
+                           struct file *file_out, loff_t pos_out,
+                           u64 len, unsigned int flags, enum ovl_copyop op)
+{
+       struct inode *inode_out = file_inode(file_out);
+       struct fd real_in, real_out;
+       const struct cred *old_cred;
+       ssize_t ret;
+
+       ret = ovl_real_fdget(file_out, &real_out);
+       if (ret)
+               return ret;
+
+       ret = ovl_real_fdget(file_in, &real_in);
+       if (ret) {
+               fdput(real_out);
+               return ret;
+       }
+
+       old_cred = ovl_override_creds(file_inode(file_out)->i_sb);
+       switch (op) {
+       case OVL_COPY:
+               ret = vfs_copy_file_range(real_in.file, pos_in,
+                                         real_out.file, pos_out, len, flags);
+               break;
+
+       case OVL_CLONE:
+               ret = vfs_clone_file_range(real_in.file, pos_in,
+                                          real_out.file, pos_out, len);
+               break;
+
+       case OVL_DEDUPE:
+               ret = vfs_dedupe_file_range_one(real_in.file, pos_in,
+                                               real_out.file, pos_out, len);
+               break;
+       }
+       revert_creds(old_cred);
+
+       /* Update size */
+       ovl_copyattr(ovl_inode_real(inode_out), inode_out);
+
+       fdput(real_in);
+       fdput(real_out);
+
+       return ret;
+}
+
+static ssize_t ovl_copy_file_range(struct file *file_in, loff_t pos_in,
+                                  struct file *file_out, loff_t pos_out,
+                                  size_t len, unsigned int flags)
+{
+       return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, flags,
+                           OVL_COPY);
+}
+
+static int ovl_clone_file_range(struct file *file_in, loff_t pos_in,
+                               struct file *file_out, loff_t pos_out, u64 len)
+{
+       return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, 0,
+                           OVL_CLONE);
+}
+
+static int ovl_dedupe_file_range(struct file *file_in, loff_t pos_in,
+                                struct file *file_out, loff_t pos_out, u64 len)
+{
+       /*
+        * Don't copy up because of a dedupe request, this wouldn't make sense
+        * most of the time (data would be duplicated instead of deduplicated).
+        */
+       if (!ovl_inode_upper(file_inode(file_in)) ||
+           !ovl_inode_upper(file_inode(file_out)))
+               return -EPERM;
+
+       return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, 0,
+                           OVL_DEDUPE);
+}
+
 const struct file_operations ovl_file_operations = {
        .open           = ovl_open,
        .release        = ovl_release,
@@ -400,4 +483,8 @@ const struct file_operations ovl_file_operations = {
        .fallocate      = ovl_fallocate,
        .unlocked_ioctl = ovl_ioctl,
        .compat_ioctl   = ovl_compat_ioctl,
+
+       .copy_file_range        = ovl_copy_file_range,
+       .clone_file_range       = ovl_clone_file_range,
+       .dedupe_file_range      = ovl_dedupe_file_range,
 };