@@ -393,7 +393,7 @@ static int au_cp_regular(struct au_cp_generic *cpg)
.label = &&out_src
}
};
- struct super_block *sb;
+ struct super_block *sb, *h_src_sb;
struct inode *h_src_inode;
struct task_struct *tsk = current;
@@ -411,9 +411,28 @@ static int au_cp_regular(struct au_cp_generic *cpg)
/* try stopping to update while we copyup */
h_src_inode = d_inode(file[SRC].dentry);
- if (!au_test_nfs(h_src_inode->i_sb))
+ h_src_sb = h_src_inode->i_sb;
+ if (!au_test_nfs(h_src_sb))
IMustLock(h_src_inode);
- err = au_copy_file(file[DST].file, file[SRC].file, cpg->len);
+
+ if (h_src_sb != file_inode(file[DST].file)->i_sb
+ || !file[DST].file->f_op->clone_file_range)
+ err = au_copy_file(file[DST].file, file[SRC].file, cpg->len);
+ else {
+ if (!au_test_nfs(h_src_sb)) {
+ inode_unlock(h_src_inode);
+ err = vfsub_clone_file_range(file[SRC].file,
+ file[DST].file, cpg->len);
+ inode_lock(h_src_inode);
+ } else
+ err = vfsub_clone_file_range(file[SRC].file,
+ file[DST].file, cpg->len);
+ if (unlikely(err == -EOPNOTSUPP && au_test_nfs(h_src_sb)))
+ /* the backend fs on NFS may not support cloning */
+ err = au_copy_file(file[DST].file, file[SRC].file,
+ cpg->len);
+ AuTraceErr(err);
+ }
/* i wonder if we had O_NO_DELAY_FPUT flag */
if (tsk->flags & PF_KTHREAD)
@@ -266,6 +266,22 @@ int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr,
struct file *h_file);
int vfsub_fsync(struct file *file, struct path *path, int datasync);
+/*
+ * re-use branch fs's ioctl(FICLONE) while aufs itself doesn't support such
+ * ioctl.
+ */
+static inline int vfsub_clone_file_range(struct file *src, struct file *dst,
+ u64 len)
+{
+ int err;
+
+ lockdep_off();
+ err = vfs_clone_file_range(src, 0, dst, 0, len);
+ lockdep_on();
+
+ return err;
+}
+
/* ---------------------------------------------------------------------- */
static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin)