aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/ioctl.c
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/ioctl.c
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/ioctl.c')
-rw-r--r--fs/cifs/ioctl.c126
1 files changed, 59 insertions, 67 deletions
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)