diff options
author | Sachin Prabhu <sprabhu@redhat.com> | 2017-02-10 05:33:51 -0500 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2017-04-07 09:04:41 -0400 |
commit | 620d8745b35daaf507186c26b40c7ea02aed131e (patch) | |
tree | a634c2ac96c3920c979869e66eb28ff032406a51 | |
parent | 312bbc5946c4b73dfc1d64c1dd5b0f9df8016587 (diff) |
Introduce cifs_copy_file_range()
The earlier changes to copy range for cifs unintentionally disabled the more
common form of server side copy.
The patch introduces the file_operations helper cifs_copy_file_range()
which is used by the syscall copy_file_range. The new file operations
helper allows us to perform server side copies for SMB2.0 and 2.1
servers as well as SMB 3.0+ servers which do not support the ioctl
FSCTL_DUPLICATE_EXTENTS_TO_FILE.
The new helper uses the ioctl FSCTL_SRV_COPYCHUNK_WRITE to perform
server side copies. The helper is called by vfs_copy_file_range() only
once an attempt to clone the file using the ioctl
FSCTL_DUPLICATE_EXTENTS_TO_FILE has failed.
Signed-off-by: Sachin Prabhu <sprabhu@redhat.com>
Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
CC: Stable <stable@vger.kernel.org>
Signed-off-by: Steve French <smfrench@gmail.com>
-rw-r--r-- | fs/cifs/cifsfs.c | 87 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 5 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 6 | ||||
-rw-r--r-- | fs/cifs/ioctl.c | 60 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 20 |
5 files changed, 110 insertions, 68 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 15e1db8738ae..dd3f5fabfdf6 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c | |||
@@ -972,6 +972,86 @@ out: | |||
972 | return rc; | 972 | return rc; |
973 | } | 973 | } |
974 | 974 | ||
975 | ssize_t cifs_file_copychunk_range(unsigned int xid, | ||
976 | struct file *src_file, loff_t off, | ||
977 | struct file *dst_file, loff_t destoff, | ||
978 | size_t len, unsigned int flags) | ||
979 | { | ||
980 | struct inode *src_inode = file_inode(src_file); | ||
981 | struct inode *target_inode = file_inode(dst_file); | ||
982 | struct cifsFileInfo *smb_file_src; | ||
983 | struct cifsFileInfo *smb_file_target; | ||
984 | struct cifs_tcon *src_tcon; | ||
985 | struct cifs_tcon *target_tcon; | ||
986 | ssize_t rc; | ||
987 | |||
988 | cifs_dbg(FYI, "copychunk range\n"); | ||
989 | |||
990 | if (src_inode == target_inode) { | ||
991 | rc = -EINVAL; | ||
992 | goto out; | ||
993 | } | ||
994 | |||
995 | if (!src_file->private_data || !dst_file->private_data) { | ||
996 | rc = -EBADF; | ||
997 | cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); | ||
998 | goto out; | ||
999 | } | ||
1000 | |||
1001 | rc = -EXDEV; | ||
1002 | smb_file_target = dst_file->private_data; | ||
1003 | smb_file_src = src_file->private_data; | ||
1004 | src_tcon = tlink_tcon(smb_file_src->tlink); | ||
1005 | target_tcon = tlink_tcon(smb_file_target->tlink); | ||
1006 | |||
1007 | if (src_tcon->ses != target_tcon->ses) { | ||
1008 | cifs_dbg(VFS, "source and target of copy not on same server\n"); | ||
1009 | goto out; | ||
1010 | } | ||
1011 | |||
1012 | /* | ||
1013 | * Note: cifs case is easier than btrfs since server responsible for | ||
1014 | * checks for proper open modes and file type and if it wants | ||
1015 | * server could even support copy of range where source = target | ||
1016 | */ | ||
1017 | lock_two_nondirectories(target_inode, src_inode); | ||
1018 | |||
1019 | cifs_dbg(FYI, "about to flush pages\n"); | ||
1020 | /* should we flush first and last page first */ | ||
1021 | truncate_inode_pages(&target_inode->i_data, 0); | ||
1022 | |||
1023 | if (target_tcon->ses->server->ops->copychunk_range) | ||
1024 | rc = target_tcon->ses->server->ops->copychunk_range(xid, | ||
1025 | smb_file_src, smb_file_target, off, len, destoff); | ||
1026 | else | ||
1027 | rc = -EOPNOTSUPP; | ||
1028 | |||
1029 | /* force revalidate of size and timestamps of target file now | ||
1030 | * that target is updated on the server | ||
1031 | */ | ||
1032 | CIFS_I(target_inode)->time = 0; | ||
1033 | /* although unlocking in the reverse order from locking is not | ||
1034 | * strictly necessary here it is a little cleaner to be consistent | ||
1035 | */ | ||
1036 | unlock_two_nondirectories(src_inode, target_inode); | ||
1037 | |||
1038 | out: | ||
1039 | return rc; | ||
1040 | } | ||
1041 | |||
1042 | static ssize_t cifs_copy_file_range(struct file *src_file, loff_t off, | ||
1043 | struct file *dst_file, loff_t destoff, | ||
1044 | size_t len, unsigned int flags) | ||
1045 | { | ||
1046 | unsigned int xid = get_xid(); | ||
1047 | ssize_t rc; | ||
1048 | |||
1049 | rc = cifs_file_copychunk_range(xid, src_file, off, dst_file, destoff, | ||
1050 | len, flags); | ||
1051 | free_xid(xid); | ||
1052 | return rc; | ||
1053 | } | ||
1054 | |||
975 | const struct file_operations cifs_file_ops = { | 1055 | const struct file_operations cifs_file_ops = { |
976 | .read_iter = cifs_loose_read_iter, | 1056 | .read_iter = cifs_loose_read_iter, |
977 | .write_iter = cifs_file_write_iter, | 1057 | .write_iter = cifs_file_write_iter, |
@@ -984,6 +1064,7 @@ const struct file_operations cifs_file_ops = { | |||
984 | .splice_read = generic_file_splice_read, | 1064 | .splice_read = generic_file_splice_read, |
985 | .llseek = cifs_llseek, | 1065 | .llseek = cifs_llseek, |
986 | .unlocked_ioctl = cifs_ioctl, | 1066 | .unlocked_ioctl = cifs_ioctl, |
1067 | .copy_file_range = cifs_copy_file_range, | ||
987 | .clone_file_range = cifs_clone_file_range, | 1068 | .clone_file_range = cifs_clone_file_range, |
988 | .setlease = cifs_setlease, | 1069 | .setlease = cifs_setlease, |
989 | .fallocate = cifs_fallocate, | 1070 | .fallocate = cifs_fallocate, |
@@ -1001,6 +1082,7 @@ const struct file_operations cifs_file_strict_ops = { | |||
1001 | .splice_read = generic_file_splice_read, | 1082 | .splice_read = generic_file_splice_read, |
1002 | .llseek = cifs_llseek, | 1083 | .llseek = cifs_llseek, |
1003 | .unlocked_ioctl = cifs_ioctl, | 1084 | .unlocked_ioctl = cifs_ioctl, |
1085 | .copy_file_range = cifs_copy_file_range, | ||
1004 | .clone_file_range = cifs_clone_file_range, | 1086 | .clone_file_range = cifs_clone_file_range, |
1005 | .setlease = cifs_setlease, | 1087 | .setlease = cifs_setlease, |
1006 | .fallocate = cifs_fallocate, | 1088 | .fallocate = cifs_fallocate, |
@@ -1018,6 +1100,7 @@ const struct file_operations cifs_file_direct_ops = { | |||
1018 | .mmap = cifs_file_mmap, | 1100 | .mmap = cifs_file_mmap, |
1019 | .splice_read = generic_file_splice_read, | 1101 | .splice_read = generic_file_splice_read, |
1020 | .unlocked_ioctl = cifs_ioctl, | 1102 | .unlocked_ioctl = cifs_ioctl, |
1103 | .copy_file_range = cifs_copy_file_range, | ||
1021 | .clone_file_range = cifs_clone_file_range, | 1104 | .clone_file_range = cifs_clone_file_range, |
1022 | .llseek = cifs_llseek, | 1105 | .llseek = cifs_llseek, |
1023 | .setlease = cifs_setlease, | 1106 | .setlease = cifs_setlease, |
@@ -1035,6 +1118,7 @@ const struct file_operations cifs_file_nobrl_ops = { | |||
1035 | .splice_read = generic_file_splice_read, | 1118 | .splice_read = generic_file_splice_read, |
1036 | .llseek = cifs_llseek, | 1119 | .llseek = cifs_llseek, |
1037 | .unlocked_ioctl = cifs_ioctl, | 1120 | .unlocked_ioctl = cifs_ioctl, |
1121 | .copy_file_range = cifs_copy_file_range, | ||
1038 | .clone_file_range = cifs_clone_file_range, | 1122 | .clone_file_range = cifs_clone_file_range, |
1039 | .setlease = cifs_setlease, | 1123 | .setlease = cifs_setlease, |
1040 | .fallocate = cifs_fallocate, | 1124 | .fallocate = cifs_fallocate, |
@@ -1051,6 +1135,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = { | |||
1051 | .splice_read = generic_file_splice_read, | 1135 | .splice_read = generic_file_splice_read, |
1052 | .llseek = cifs_llseek, | 1136 | .llseek = cifs_llseek, |
1053 | .unlocked_ioctl = cifs_ioctl, | 1137 | .unlocked_ioctl = cifs_ioctl, |
1138 | .copy_file_range = cifs_copy_file_range, | ||
1054 | .clone_file_range = cifs_clone_file_range, | 1139 | .clone_file_range = cifs_clone_file_range, |
1055 | .setlease = cifs_setlease, | 1140 | .setlease = cifs_setlease, |
1056 | .fallocate = cifs_fallocate, | 1141 | .fallocate = cifs_fallocate, |
@@ -1067,6 +1152,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = { | |||
1067 | .mmap = cifs_file_mmap, | 1152 | .mmap = cifs_file_mmap, |
1068 | .splice_read = generic_file_splice_read, | 1153 | .splice_read = generic_file_splice_read, |
1069 | .unlocked_ioctl = cifs_ioctl, | 1154 | .unlocked_ioctl = cifs_ioctl, |
1155 | .copy_file_range = cifs_copy_file_range, | ||
1070 | .clone_file_range = cifs_clone_file_range, | 1156 | .clone_file_range = cifs_clone_file_range, |
1071 | .llseek = cifs_llseek, | 1157 | .llseek = cifs_llseek, |
1072 | .setlease = cifs_setlease, | 1158 | .setlease = cifs_setlease, |
@@ -1078,6 +1164,7 @@ const struct file_operations cifs_dir_ops = { | |||
1078 | .release = cifs_closedir, | 1164 | .release = cifs_closedir, |
1079 | .read = generic_read_dir, | 1165 | .read = generic_read_dir, |
1080 | .unlocked_ioctl = cifs_ioctl, | 1166 | .unlocked_ioctl = cifs_ioctl, |
1167 | .copy_file_range = cifs_copy_file_range, | ||
1081 | .clone_file_range = cifs_clone_file_range, | 1168 | .clone_file_range = cifs_clone_file_range, |
1082 | .llseek = generic_file_llseek, | 1169 | .llseek = generic_file_llseek, |
1083 | }; | 1170 | }; |
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index da717fee3026..30bf89b1fd9a 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h | |||
@@ -139,6 +139,11 @@ extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); | |||
139 | # define cifs_listxattr NULL | 139 | # define cifs_listxattr NULL |
140 | #endif | 140 | #endif |
141 | 141 | ||
142 | extern ssize_t cifs_file_copychunk_range(unsigned int xid, | ||
143 | struct file *src_file, loff_t off, | ||
144 | struct file *dst_file, loff_t destoff, | ||
145 | size_t len, unsigned int flags); | ||
146 | |||
142 | extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); | 147 | extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); |
143 | #ifdef CONFIG_CIFS_NFSD_EXPORT | 148 | #ifdef CONFIG_CIFS_NFSD_EXPORT |
144 | extern const struct export_operations cifs_export_ops; | 149 | extern const struct export_operations cifs_export_ops; |
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 57c594827cb3..d07f13a63369 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
@@ -408,10 +408,10 @@ struct smb_version_operations { | |||
408 | char * (*create_lease_buf)(u8 *, u8); | 408 | char * (*create_lease_buf)(u8 *, u8); |
409 | /* parse lease context buffer and return oplock/epoch info */ | 409 | /* parse lease context buffer and return oplock/epoch info */ |
410 | __u8 (*parse_lease_buf)(void *, unsigned int *); | 410 | __u8 (*parse_lease_buf)(void *, unsigned int *); |
411 | int (*copychunk_range)(const unsigned int, | 411 | ssize_t (*copychunk_range)(const unsigned int, |
412 | struct cifsFileInfo *src_file, | 412 | struct cifsFileInfo *src_file, |
413 | struct cifsFileInfo *target_file, u64 src_off, u64 len, | 413 | struct cifsFileInfo *target_file, |
414 | u64 dest_off); | 414 | u64 src_off, u64 len, u64 dest_off); |
415 | int (*duplicate_extents)(const unsigned int, struct cifsFileInfo *src, | 415 | int (*duplicate_extents)(const unsigned int, struct cifsFileInfo *src, |
416 | struct cifsFileInfo *target_file, u64 src_off, u64 len, | 416 | struct cifsFileInfo *target_file, u64 src_off, u64 len, |
417 | u64 dest_off); | 417 | u64 dest_off); |
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 9bf0f94fae63..265c45fe4ea5 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c | |||
@@ -34,63 +34,6 @@ | |||
34 | #include "cifs_ioctl.h" | 34 | #include "cifs_ioctl.h" |
35 | #include <linux/btrfs.h> | 35 | #include <linux/btrfs.h> |
36 | 36 | ||
37 | static int cifs_file_copychunk_range(unsigned int xid, struct file *src_file, | ||
38 | struct file *dst_file) | ||
39 | { | ||
40 | struct inode *src_inode = file_inode(src_file); | ||
41 | struct inode *target_inode = file_inode(dst_file); | ||
42 | struct cifsFileInfo *smb_file_src; | ||
43 | struct cifsFileInfo *smb_file_target; | ||
44 | struct cifs_tcon *src_tcon; | ||
45 | struct cifs_tcon *target_tcon; | ||
46 | int rc; | ||
47 | |||
48 | cifs_dbg(FYI, "ioctl copychunk range\n"); | ||
49 | |||
50 | if (!src_file->private_data || !dst_file->private_data) { | ||
51 | rc = -EBADF; | ||
52 | cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); | ||
53 | goto out; | ||
54 | } | ||
55 | |||
56 | rc = -EXDEV; | ||
57 | smb_file_target = dst_file->private_data; | ||
58 | smb_file_src = src_file->private_data; | ||
59 | src_tcon = tlink_tcon(smb_file_src->tlink); | ||
60 | target_tcon = tlink_tcon(smb_file_target->tlink); | ||
61 | |||
62 | if (src_tcon->ses != target_tcon->ses) { | ||
63 | cifs_dbg(VFS, "source and target of copy not on same server\n"); | ||
64 | goto out; | ||
65 | } | ||
66 | |||
67 | /* | ||
68 | * Note: cifs case is easier than btrfs since server responsible for | ||
69 | * checks for proper open modes and file type and if it wants | ||
70 | * server could even support copy of range where source = target | ||
71 | */ | ||
72 | lock_two_nondirectories(target_inode, src_inode); | ||
73 | |||
74 | cifs_dbg(FYI, "about to flush pages\n"); | ||
75 | /* should we flush first and last page first */ | ||
76 | truncate_inode_pages(&target_inode->i_data, 0); | ||
77 | |||
78 | if (target_tcon->ses->server->ops->copychunk_range) | ||
79 | rc = target_tcon->ses->server->ops->copychunk_range(xid, | ||
80 | smb_file_src, smb_file_target, 0, src_inode->i_size, 0); | ||
81 | else | ||
82 | rc = -EOPNOTSUPP; | ||
83 | |||
84 | /* force revalidate of size and timestamps of target file now | ||
85 | that target is updated on the server */ | ||
86 | CIFS_I(target_inode)->time = 0; | ||
87 | /* although unlocking in the reverse order from locking is not | ||
88 | strictly necessary here it is a little cleaner to be consistent */ | ||
89 | unlock_two_nondirectories(src_inode, target_inode); | ||
90 | out: | ||
91 | return rc; | ||
92 | } | ||
93 | |||
94 | static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file, | 37 | static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file, |
95 | unsigned long srcfd) | 38 | unsigned long srcfd) |
96 | { | 39 | { |
@@ -129,7 +72,8 @@ static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file, | |||
129 | if (S_ISDIR(src_inode->i_mode)) | 72 | if (S_ISDIR(src_inode->i_mode)) |
130 | goto out_fput; | 73 | goto out_fput; |
131 | 74 | ||
132 | rc = cifs_file_copychunk_range(xid, src_file.file, dst_file); | 75 | rc = cifs_file_copychunk_range(xid, src_file.file, 0, dst_file, 0, |
76 | src_inode->i_size, 0); | ||
133 | 77 | ||
134 | out_fput: | 78 | out_fput: |
135 | fdput(src_file); | 79 | fdput(src_file); |
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 3f12e0992b9b..063e59d543f9 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -592,7 +592,7 @@ req_res_key_exit: | |||
592 | return rc; | 592 | return rc; |
593 | } | 593 | } |
594 | 594 | ||
595 | static int | 595 | static ssize_t |
596 | smb2_copychunk_range(const unsigned int xid, | 596 | smb2_copychunk_range(const unsigned int xid, |
597 | struct cifsFileInfo *srcfile, | 597 | struct cifsFileInfo *srcfile, |
598 | struct cifsFileInfo *trgtfile, u64 src_off, | 598 | struct cifsFileInfo *trgtfile, u64 src_off, |
@@ -605,6 +605,7 @@ smb2_copychunk_range(const unsigned int xid, | |||
605 | struct cifs_tcon *tcon; | 605 | struct cifs_tcon *tcon; |
606 | int chunks_copied = 0; | 606 | int chunks_copied = 0; |
607 | bool chunk_sizes_updated = false; | 607 | bool chunk_sizes_updated = false; |
608 | ssize_t bytes_written, total_bytes_written = 0; | ||
608 | 609 | ||
609 | pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); | 610 | pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); |
610 | 611 | ||
@@ -669,14 +670,16 @@ smb2_copychunk_range(const unsigned int xid, | |||
669 | } | 670 | } |
670 | chunks_copied++; | 671 | chunks_copied++; |
671 | 672 | ||
672 | src_off += le32_to_cpu(retbuf->TotalBytesWritten); | 673 | bytes_written = le32_to_cpu(retbuf->TotalBytesWritten); |
673 | dest_off += le32_to_cpu(retbuf->TotalBytesWritten); | 674 | src_off += bytes_written; |
674 | len -= le32_to_cpu(retbuf->TotalBytesWritten); | 675 | dest_off += bytes_written; |
676 | len -= bytes_written; | ||
677 | total_bytes_written += bytes_written; | ||
675 | 678 | ||
676 | cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %d\n", | 679 | cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %zu\n", |
677 | le32_to_cpu(retbuf->ChunksWritten), | 680 | le32_to_cpu(retbuf->ChunksWritten), |
678 | le32_to_cpu(retbuf->ChunkBytesWritten), | 681 | le32_to_cpu(retbuf->ChunkBytesWritten), |
679 | le32_to_cpu(retbuf->TotalBytesWritten)); | 682 | bytes_written); |
680 | } else if (rc == -EINVAL) { | 683 | } else if (rc == -EINVAL) { |
681 | if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) | 684 | if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) |
682 | goto cchunk_out; | 685 | goto cchunk_out; |
@@ -713,7 +716,10 @@ smb2_copychunk_range(const unsigned int xid, | |||
713 | cchunk_out: | 716 | cchunk_out: |
714 | kfree(pcchunk); | 717 | kfree(pcchunk); |
715 | kfree(retbuf); | 718 | kfree(retbuf); |
716 | return rc; | 719 | if (rc) |
720 | return rc; | ||
721 | else | ||
722 | return total_bytes_written; | ||
717 | } | 723 | } |
718 | 724 | ||
719 | static int | 725 | static int |