|
@@ -914,6 +914,61 @@ const struct inode_operations cifs_symlink_inode_ops = {
|
|
#endif
|
|
#endif
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static int cifs_clone_file_range(struct file *src_file, loff_t off,
|
|
|
|
+ struct file *dst_file, loff_t destoff, u64 len)
|
|
|
|
+{
|
|
|
|
+ struct inode *src_inode = file_inode(src_file);
|
|
|
|
+ struct inode *target_inode = file_inode(dst_file);
|
|
|
|
+ struct cifsFileInfo *smb_file_src = src_file->private_data;
|
|
|
|
+ struct cifsFileInfo *smb_file_target = dst_file->private_data;
|
|
|
|
+ struct cifs_tcon *src_tcon = tlink_tcon(smb_file_src->tlink);
|
|
|
|
+ struct cifs_tcon *target_tcon = tlink_tcon(smb_file_target->tlink);
|
|
|
|
+ unsigned int xid;
|
|
|
|
+ int rc;
|
|
|
|
+
|
|
|
|
+ cifs_dbg(FYI, "clone range\n");
|
|
|
|
+
|
|
|
|
+ xid = get_xid();
|
|
|
|
+
|
|
|
|
+ if (!src_file->private_data || !dst_file->private_data) {
|
|
|
|
+ rc = -EBADF;
|
|
|
|
+ cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Note: cifs case is easier than btrfs since server responsible for
|
|
|
|
+ * checks for proper open modes and file type and if it wants
|
|
|
|
+ * server could even support copy of range where source = target
|
|
|
|
+ */
|
|
|
|
+ lock_two_nondirectories(target_inode, src_inode);
|
|
|
|
+
|
|
|
|
+ if (len == 0)
|
|
|
|
+ len = src_inode->i_size - off;
|
|
|
|
+
|
|
|
|
+ cifs_dbg(FYI, "about to flush pages\n");
|
|
|
|
+ /* should we flush first and last page first */
|
|
|
|
+ truncate_inode_pages_range(&target_inode->i_data, destoff,
|
|
|
|
+ PAGE_CACHE_ALIGN(destoff + len)-1);
|
|
|
|
+
|
|
|
|
+ if (target_tcon->ses->server->ops->duplicate_extents)
|
|
|
|
+ rc = target_tcon->ses->server->ops->duplicate_extents(xid,
|
|
|
|
+ smb_file_src, smb_file_target, off, len, destoff);
|
|
|
|
+ else
|
|
|
|
+ rc = -EOPNOTSUPP;
|
|
|
|
+
|
|
|
|
+ /* force revalidate of size and timestamps of target file now
|
|
|
|
+ that target is updated on the server */
|
|
|
|
+ CIFS_I(target_inode)->time = 0;
|
|
|
|
+out_unlock:
|
|
|
|
+ /* although unlocking in the reverse order from locking is not
|
|
|
|
+ strictly necessary here it is a little cleaner to be consistent */
|
|
|
|
+ unlock_two_nondirectories(src_inode, target_inode);
|
|
|
|
+out:
|
|
|
|
+ free_xid(xid);
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
const struct file_operations cifs_file_ops = {
|
|
const struct file_operations cifs_file_ops = {
|
|
.read_iter = cifs_loose_read_iter,
|
|
.read_iter = cifs_loose_read_iter,
|
|
.write_iter = cifs_file_write_iter,
|
|
.write_iter = cifs_file_write_iter,
|
|
@@ -926,6 +981,7 @@ const struct file_operations cifs_file_ops = {
|
|
.splice_read = generic_file_splice_read,
|
|
.splice_read = generic_file_splice_read,
|
|
.llseek = cifs_llseek,
|
|
.llseek = cifs_llseek,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
|
|
+ .clone_file_range = cifs_clone_file_range,
|
|
.setlease = cifs_setlease,
|
|
.setlease = cifs_setlease,
|
|
.fallocate = cifs_fallocate,
|
|
.fallocate = cifs_fallocate,
|
|
};
|
|
};
|
|
@@ -942,6 +998,8 @@ const struct file_operations cifs_file_strict_ops = {
|
|
.splice_read = generic_file_splice_read,
|
|
.splice_read = generic_file_splice_read,
|
|
.llseek = cifs_llseek,
|
|
.llseek = cifs_llseek,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
|
|
+ .clone_file_range = cifs_clone_file_range,
|
|
|
|
+ .clone_file_range = cifs_clone_file_range,
|
|
.setlease = cifs_setlease,
|
|
.setlease = cifs_setlease,
|
|
.fallocate = cifs_fallocate,
|
|
.fallocate = cifs_fallocate,
|
|
};
|
|
};
|
|
@@ -958,6 +1016,7 @@ const struct file_operations cifs_file_direct_ops = {
|
|
.mmap = cifs_file_mmap,
|
|
.mmap = cifs_file_mmap,
|
|
.splice_read = generic_file_splice_read,
|
|
.splice_read = generic_file_splice_read,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
|
|
+ .clone_file_range = cifs_clone_file_range,
|
|
.llseek = cifs_llseek,
|
|
.llseek = cifs_llseek,
|
|
.setlease = cifs_setlease,
|
|
.setlease = cifs_setlease,
|
|
.fallocate = cifs_fallocate,
|
|
.fallocate = cifs_fallocate,
|
|
@@ -974,6 +1033,7 @@ const struct file_operations cifs_file_nobrl_ops = {
|
|
.splice_read = generic_file_splice_read,
|
|
.splice_read = generic_file_splice_read,
|
|
.llseek = cifs_llseek,
|
|
.llseek = cifs_llseek,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
|
|
+ .clone_file_range = cifs_clone_file_range,
|
|
.setlease = cifs_setlease,
|
|
.setlease = cifs_setlease,
|
|
.fallocate = cifs_fallocate,
|
|
.fallocate = cifs_fallocate,
|
|
};
|
|
};
|
|
@@ -989,6 +1049,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
|
|
.splice_read = generic_file_splice_read,
|
|
.splice_read = generic_file_splice_read,
|
|
.llseek = cifs_llseek,
|
|
.llseek = cifs_llseek,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
|
|
+ .clone_file_range = cifs_clone_file_range,
|
|
.setlease = cifs_setlease,
|
|
.setlease = cifs_setlease,
|
|
.fallocate = cifs_fallocate,
|
|
.fallocate = cifs_fallocate,
|
|
};
|
|
};
|
|
@@ -1004,6 +1065,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = {
|
|
.mmap = cifs_file_mmap,
|
|
.mmap = cifs_file_mmap,
|
|
.splice_read = generic_file_splice_read,
|
|
.splice_read = generic_file_splice_read,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
|
|
+ .clone_file_range = cifs_clone_file_range,
|
|
.llseek = cifs_llseek,
|
|
.llseek = cifs_llseek,
|
|
.setlease = cifs_setlease,
|
|
.setlease = cifs_setlease,
|
|
.fallocate = cifs_fallocate,
|
|
.fallocate = cifs_fallocate,
|
|
@@ -1014,6 +1076,7 @@ const struct file_operations cifs_dir_ops = {
|
|
.release = cifs_closedir,
|
|
.release = cifs_closedir,
|
|
.read = generic_read_dir,
|
|
.read = generic_read_dir,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
.unlocked_ioctl = cifs_ioctl,
|
|
|
|
+ .clone_file_range = cifs_clone_file_range,
|
|
.llseek = generic_file_llseek,
|
|
.llseek = generic_file_llseek,
|
|
};
|
|
};
|
|
|
|
|