aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2015-12-03 06:59:50 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2015-12-07 23:11:33 -0500
commit04b38d601239b4d9be641b412cf4b7456a041c67 (patch)
tree196b5fa72848de2a98e09af86099d99da70f2833 /fs/cifs
parentacc15575e78e534c12549d8057a692f490a50f61 (diff)
vfs: pull btrfs clone API to vfs layer
The btrfs clone ioctls are now adopted by other file systems, with NFS and CIFS already having support for them, and XFS being under active development. To avoid growth of various slightly incompatible implementations, add one to the VFS. Note that clones are different from file copies in several ways: - they are atomic vs other writers - they support whole file clones - they support 64-bit legth clones - they do not allow partial success (aka short writes) - clones are expected to be a fast metadata operation Because of that it would be rather cumbersome to try to piggyback them on top of the recent clone_file_range infrastructure. The converse isn't true and the clone_file_range system call could try clone file range as a first attempt to copy, something that further patches will enable. Based on earlier work from Peng Tao. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/cifsfs.c63
-rw-r--r--fs/cifs/cifsfs.h1
-rw-r--r--fs/cifs/ioctl.c126
3 files changed, 122 insertions, 68 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index cbc0f4bca0c0..e9b978f2e114 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -914,6 +914,61 @@ const struct inode_operations cifs_symlink_inode_ops = {
914#endif 914#endif
915}; 915};
916 916
917static int cifs_clone_file_range(struct file *src_file, loff_t off,
918 struct file *dst_file, loff_t destoff, u64 len)
919{
920 struct inode *src_inode = file_inode(src_file);
921 struct inode *target_inode = file_inode(dst_file);
922 struct cifsFileInfo *smb_file_src = src_file->private_data;
923 struct cifsFileInfo *smb_file_target = dst_file->private_data;
924 struct cifs_tcon *src_tcon = tlink_tcon(smb_file_src->tlink);
925 struct cifs_tcon *target_tcon = tlink_tcon(smb_file_target->tlink);
926 unsigned int xid;
927 int rc;
928
929 cifs_dbg(FYI, "clone range\n");
930
931 xid = get_xid();
932
933 if (!src_file->private_data || !dst_file->private_data) {
934 rc = -EBADF;
935 cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
936 goto out;
937 }
938
939 /*
940 * Note: cifs case is easier than btrfs since server responsible for
941 * checks for proper open modes and file type and if it wants
942 * server could even support copy of range where source = target
943 */
944 lock_two_nondirectories(target_inode, src_inode);
945
946 if (len == 0)
947 len = src_inode->i_size - off;
948
949 cifs_dbg(FYI, "about to flush pages\n");
950 /* should we flush first and last page first */
951 truncate_inode_pages_range(&target_inode->i_data, destoff,
952 PAGE_CACHE_ALIGN(destoff + len)-1);
953
954 if (target_tcon->ses->server->ops->duplicate_extents)
955 rc = target_tcon->ses->server->ops->duplicate_extents(xid,
956 smb_file_src, smb_file_target, off, len, destoff);
957 else
958 rc = -EOPNOTSUPP;
959
960 /* force revalidate of size and timestamps of target file now
961 that target is updated on the server */
962 CIFS_I(target_inode)->time = 0;
963out_unlock:
964 /* although unlocking in the reverse order from locking is not
965 strictly necessary here it is a little cleaner to be consistent */
966 unlock_two_nondirectories(src_inode, target_inode);
967out:
968 free_xid(xid);
969 return rc;
970}
971
917const struct file_operations cifs_file_ops = { 972const struct file_operations cifs_file_ops = {
918 .read_iter = cifs_loose_read_iter, 973 .read_iter = cifs_loose_read_iter,
919 .write_iter = cifs_file_write_iter, 974 .write_iter = cifs_file_write_iter,
@@ -926,6 +981,7 @@ const struct file_operations cifs_file_ops = {
926 .splice_read = generic_file_splice_read, 981 .splice_read = generic_file_splice_read,
927 .llseek = cifs_llseek, 982 .llseek = cifs_llseek,
928 .unlocked_ioctl = cifs_ioctl, 983 .unlocked_ioctl = cifs_ioctl,
984 .clone_file_range = cifs_clone_file_range,
929 .setlease = cifs_setlease, 985 .setlease = cifs_setlease,
930 .fallocate = cifs_fallocate, 986 .fallocate = cifs_fallocate,
931}; 987};
@@ -942,6 +998,8 @@ const struct file_operations cifs_file_strict_ops = {
942 .splice_read = generic_file_splice_read, 998 .splice_read = generic_file_splice_read,
943 .llseek = cifs_llseek, 999 .llseek = cifs_llseek,
944 .unlocked_ioctl = cifs_ioctl, 1000 .unlocked_ioctl = cifs_ioctl,
1001 .clone_file_range = cifs_clone_file_range,
1002 .clone_file_range = cifs_clone_file_range,
945 .setlease = cifs_setlease, 1003 .setlease = cifs_setlease,
946 .fallocate = cifs_fallocate, 1004 .fallocate = cifs_fallocate,
947}; 1005};
@@ -958,6 +1016,7 @@ const struct file_operations cifs_file_direct_ops = {
958 .mmap = cifs_file_mmap, 1016 .mmap = cifs_file_mmap,
959 .splice_read = generic_file_splice_read, 1017 .splice_read = generic_file_splice_read,
960 .unlocked_ioctl = cifs_ioctl, 1018 .unlocked_ioctl = cifs_ioctl,
1019 .clone_file_range = cifs_clone_file_range,
961 .llseek = cifs_llseek, 1020 .llseek = cifs_llseek,
962 .setlease = cifs_setlease, 1021 .setlease = cifs_setlease,
963 .fallocate = cifs_fallocate, 1022 .fallocate = cifs_fallocate,
@@ -974,6 +1033,7 @@ const struct file_operations cifs_file_nobrl_ops = {
974 .splice_read = generic_file_splice_read, 1033 .splice_read = generic_file_splice_read,
975 .llseek = cifs_llseek, 1034 .llseek = cifs_llseek,
976 .unlocked_ioctl = cifs_ioctl, 1035 .unlocked_ioctl = cifs_ioctl,
1036 .clone_file_range = cifs_clone_file_range,
977 .setlease = cifs_setlease, 1037 .setlease = cifs_setlease,
978 .fallocate = cifs_fallocate, 1038 .fallocate = cifs_fallocate,
979}; 1039};
@@ -989,6 +1049,7 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
989 .splice_read = generic_file_splice_read, 1049 .splice_read = generic_file_splice_read,
990 .llseek = cifs_llseek, 1050 .llseek = cifs_llseek,
991 .unlocked_ioctl = cifs_ioctl, 1051 .unlocked_ioctl = cifs_ioctl,
1052 .clone_file_range = cifs_clone_file_range,
992 .setlease = cifs_setlease, 1053 .setlease = cifs_setlease,
993 .fallocate = cifs_fallocate, 1054 .fallocate = cifs_fallocate,
994}; 1055};
@@ -1004,6 +1065,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = {
1004 .mmap = cifs_file_mmap, 1065 .mmap = cifs_file_mmap,
1005 .splice_read = generic_file_splice_read, 1066 .splice_read = generic_file_splice_read,
1006 .unlocked_ioctl = cifs_ioctl, 1067 .unlocked_ioctl = cifs_ioctl,
1068 .clone_file_range = cifs_clone_file_range,
1007 .llseek = cifs_llseek, 1069 .llseek = cifs_llseek,
1008 .setlease = cifs_setlease, 1070 .setlease = cifs_setlease,
1009 .fallocate = cifs_fallocate, 1071 .fallocate = cifs_fallocate,
@@ -1014,6 +1076,7 @@ const struct file_operations cifs_dir_ops = {
1014 .release = cifs_closedir, 1076 .release = cifs_closedir,
1015 .read = generic_read_dir, 1077 .read = generic_read_dir,
1016 .unlocked_ioctl = cifs_ioctl, 1078 .unlocked_ioctl = cifs_ioctl,
1079 .clone_file_range = cifs_clone_file_range,
1017 .llseek = generic_file_llseek, 1080 .llseek = generic_file_llseek,
1018}; 1081};
1019 1082
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index c3cc1609025f..c399513c3cbd 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -131,7 +131,6 @@ extern int cifs_setxattr(struct dentry *, const char *, const void *,
131extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t); 131extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
132extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); 132extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
133extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); 133extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
134
135#ifdef CONFIG_CIFS_NFSD_EXPORT 134#ifdef CONFIG_CIFS_NFSD_EXPORT
136extern const struct export_operations cifs_export_ops; 135extern const struct export_operations cifs_export_ops;
137#endif /* CONFIG_CIFS_NFSD_EXPORT */ 136#endif /* CONFIG_CIFS_NFSD_EXPORT */
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 35cf990f87d3..7a3b84e300f8 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -34,73 +34,36 @@
34#include "cifs_ioctl.h" 34#include "cifs_ioctl.h"
35#include <linux/btrfs.h> 35#include <linux/btrfs.h>
36 36
37static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file, 37static int cifs_file_clone_range(unsigned int xid, struct file *src_file,
38 unsigned long srcfd, u64 off, u64 len, u64 destoff, 38 struct file *dst_file)
39 bool dup_extents)
40{ 39{
41 int rc; 40 struct inode *src_inode = file_inode(src_file);
42 struct cifsFileInfo *smb_file_target = dst_file->private_data;
43 struct inode *target_inode = file_inode(dst_file); 41 struct inode *target_inode = file_inode(dst_file);
44 struct cifs_tcon *target_tcon;
45 struct fd src_file;
46 struct cifsFileInfo *smb_file_src; 42 struct cifsFileInfo *smb_file_src;
47 struct inode *src_inode; 43 struct cifsFileInfo *smb_file_target;
48 struct cifs_tcon *src_tcon; 44 struct cifs_tcon *src_tcon;
45 struct cifs_tcon *target_tcon;
46 int rc;
49 47
50 cifs_dbg(FYI, "ioctl clone range\n"); 48 cifs_dbg(FYI, "ioctl clone range\n");
51 /* the destination must be opened for writing */
52 if (!(dst_file->f_mode & FMODE_WRITE)) {
53 cifs_dbg(FYI, "file target not open for write\n");
54 return -EINVAL;
55 }
56 49
57 /* check if target volume is readonly and take reference */ 50 if (!src_file->private_data || !dst_file->private_data) {
58 rc = mnt_want_write_file(dst_file);
59 if (rc) {
60 cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
61 return rc;
62 }
63
64 src_file = fdget(srcfd);
65 if (!src_file.file) {
66 rc = -EBADF;
67 goto out_drop_write;
68 }
69
70 if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) {
71 rc = -EBADF;
72 cifs_dbg(VFS, "src file seems to be from a different filesystem type\n");
73 goto out_fput;
74 }
75
76 if ((!src_file.file->private_data) || (!dst_file->private_data)) {
77 rc = -EBADF; 51 rc = -EBADF;
78 cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); 52 cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
79 goto out_fput; 53 goto out;
80 } 54 }
81 55
82 rc = -EXDEV; 56 rc = -EXDEV;
83 smb_file_target = dst_file->private_data; 57 smb_file_target = dst_file->private_data;
84 smb_file_src = src_file.file->private_data; 58 smb_file_src = src_file->private_data;
85 src_tcon = tlink_tcon(smb_file_src->tlink); 59 src_tcon = tlink_tcon(smb_file_src->tlink);
86 target_tcon = tlink_tcon(smb_file_target->tlink); 60 target_tcon = tlink_tcon(smb_file_target->tlink);
87 61
88 /* check source and target on same server (or volume if dup_extents) */ 62 if (src_tcon->ses != target_tcon->ses) {
89 if (dup_extents && (src_tcon != target_tcon)) {
90 cifs_dbg(VFS, "source and target of copy not on same share\n");
91 goto out_fput;
92 }
93
94 if (!dup_extents && (src_tcon->ses != target_tcon->ses)) {
95 cifs_dbg(VFS, "source and target of copy not on same server\n"); 63 cifs_dbg(VFS, "source and target of copy not on same server\n");
96 goto out_fput; 64 goto out;
97 } 65 }
98 66
99 src_inode = file_inode(src_file.file);
100 rc = -EINVAL;
101 if (S_ISDIR(src_inode->i_mode))
102 goto out_fput;
103
104 /* 67 /*
105 * Note: cifs case is easier than btrfs since server responsible for 68 * Note: cifs case is easier than btrfs since server responsible for
106 * checks for proper open modes and file type and if it wants 69 * checks for proper open modes and file type and if it wants
@@ -108,34 +71,66 @@ static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
108 */ 71 */
109 lock_two_nondirectories(target_inode, src_inode); 72 lock_two_nondirectories(target_inode, src_inode);
110 73
111 /* determine range to clone */
112 rc = -EINVAL;
113 if (off + len > src_inode->i_size || off + len < off)
114 goto out_unlock;
115 if (len == 0)
116 len = src_inode->i_size - off;
117
118 cifs_dbg(FYI, "about to flush pages\n"); 74 cifs_dbg(FYI, "about to flush pages\n");
119 /* should we flush first and last page first */ 75 /* should we flush first and last page first */
120 truncate_inode_pages_range(&target_inode->i_data, destoff, 76 truncate_inode_pages(&target_inode->i_data, 0);
121 PAGE_CACHE_ALIGN(destoff + len)-1);
122 77
123 if (dup_extents && target_tcon->ses->server->ops->duplicate_extents) 78 if (target_tcon->ses->server->ops->clone_range)
124 rc = target_tcon->ses->server->ops->duplicate_extents(xid,
125 smb_file_src, smb_file_target, off, len, destoff);
126 else if (!dup_extents && target_tcon->ses->server->ops->clone_range)
127 rc = target_tcon->ses->server->ops->clone_range(xid, 79 rc = target_tcon->ses->server->ops->clone_range(xid,
128 smb_file_src, smb_file_target, off, len, destoff); 80 smb_file_src, smb_file_target, 0, src_inode->i_size, 0);
129 else 81 else
130 rc = -EOPNOTSUPP; 82 rc = -EOPNOTSUPP;
131 83
132 /* force revalidate of size and timestamps of target file now 84 /* force revalidate of size and timestamps of target file now
133 that target is updated on the server */ 85 that target is updated on the server */
134 CIFS_I(target_inode)->time = 0; 86 CIFS_I(target_inode)->time = 0;
135out_unlock:
136 /* although unlocking in the reverse order from locking is not 87 /* although unlocking in the reverse order from locking is not
137 strictly necessary here it is a little cleaner to be consistent */ 88 strictly necessary here it is a little cleaner to be consistent */
138 unlock_two_nondirectories(src_inode, target_inode); 89 unlock_two_nondirectories(src_inode, target_inode);
90out:
91 return rc;
92}
93
94static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
95 unsigned long srcfd)
96{
97 int rc;
98 struct fd src_file;
99 struct inode *src_inode;
100
101 cifs_dbg(FYI, "ioctl clone range\n");
102 /* the destination must be opened for writing */
103 if (!(dst_file->f_mode & FMODE_WRITE)) {
104 cifs_dbg(FYI, "file target not open for write\n");
105 return -EINVAL;
106 }
107
108 /* check if target volume is readonly and take reference */
109 rc = mnt_want_write_file(dst_file);
110 if (rc) {
111 cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
112 return rc;
113 }
114
115 src_file = fdget(srcfd);
116 if (!src_file.file) {
117 rc = -EBADF;
118 goto out_drop_write;
119 }
120
121 if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) {
122 rc = -EBADF;
123 cifs_dbg(VFS, "src file seems to be from a different filesystem type\n");
124 goto out_fput;
125 }
126
127 src_inode = file_inode(src_file.file);
128 rc = -EINVAL;
129 if (S_ISDIR(src_inode->i_mode))
130 goto out_fput;
131
132 rc = cifs_file_clone_range(xid, src_file.file, dst_file);
133
139out_fput: 134out_fput:
140 fdput(src_file); 135 fdput(src_file);
141out_drop_write: 136out_drop_write:
@@ -256,10 +251,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
256 } 251 }
257 break; 252 break;
258 case CIFS_IOC_COPYCHUNK_FILE: 253 case CIFS_IOC_COPYCHUNK_FILE:
259 rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0, false); 254 rc = cifs_ioctl_clone(xid, filep, arg);
260 break;
261 case BTRFS_IOC_CLONE:
262 rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0, true);
263 break; 255 break;
264 case CIFS_IOC_SET_INTEGRITY: 256 case CIFS_IOC_SET_INTEGRITY:
265 if (pSMBFile == NULL) 257 if (pSMBFile == NULL)