diff options
author | Peng Tao <tao.peng@primarydata.com> | 2015-09-25 14:24:36 -0400 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2015-10-15 16:08:04 -0400 |
commit | bea51b30b281039f0f43fb4f42028ddf33fb601f (patch) | |
tree | cbe3f34a5758b920289b5383ff72e2108b9fa070 | |
parent | e5341f3a5762d17be9cdd06257c02c0098bdcab8 (diff) |
nfs42: add NFS_IOC_CLONE ioctl
It can be called by user space to CLONE two files.
Follow btrfs lead and define NFS_IOC_CLONE same as BTRFS_IOC_CLONE.
Thus we don't mess up userspace with too many ioctls.
Signed-off-by: Peng Tao <tao.peng@primarydata.com>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
-rw-r--r-- | fs/nfs/nfs4file.c | 102 | ||||
-rw-r--r-- | include/uapi/linux/nfs.h | 4 |
2 files changed, 106 insertions, 0 deletions
diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index b0dbe0abed53..fc68ba5f030c 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * Copyright (C) 1992 Rick Sladkey | 4 | * Copyright (C) 1992 Rick Sladkey |
5 | */ | 5 | */ |
6 | #include <linux/fs.h> | 6 | #include <linux/fs.h> |
7 | #include <linux/file.h> | ||
7 | #include <linux/falloc.h> | 8 | #include <linux/falloc.h> |
8 | #include <linux/nfs_fs.h> | 9 | #include <linux/nfs_fs.h> |
9 | #include "delegation.h" | 10 | #include "delegation.h" |
@@ -192,8 +193,104 @@ static long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t | |||
192 | return nfs42_proc_deallocate(filep, offset, len); | 193 | return nfs42_proc_deallocate(filep, offset, len); |
193 | return nfs42_proc_allocate(filep, offset, len); | 194 | return nfs42_proc_allocate(filep, offset, len); |
194 | } | 195 | } |
196 | |||
197 | static noinline long | ||
198 | nfs42_ioctl_clone(struct file *dst_file, unsigned long srcfd, | ||
199 | u64 src_off, u64 dst_off, u64 count) | ||
200 | { | ||
201 | struct inode *dst_inode = file_inode(dst_file); | ||
202 | struct fd src_file; | ||
203 | struct inode *src_inode; | ||
204 | int ret; | ||
205 | |||
206 | /* dst file must be opened for writing */ | ||
207 | if (!(dst_file->f_mode & FMODE_WRITE)) | ||
208 | return -EINVAL; | ||
209 | |||
210 | ret = mnt_want_write_file(dst_file); | ||
211 | if (ret) | ||
212 | return ret; | ||
213 | |||
214 | src_file = fdget(srcfd); | ||
215 | if (!src_file.file) { | ||
216 | ret = -EBADF; | ||
217 | goto out_drop_write; | ||
218 | } | ||
219 | |||
220 | src_inode = file_inode(src_file.file); | ||
221 | |||
222 | /* src and dst must be different files */ | ||
223 | ret = -EINVAL; | ||
224 | if (src_inode == dst_inode) | ||
225 | goto out_fput; | ||
226 | |||
227 | /* src file must be opened for reading */ | ||
228 | if (!(src_file.file->f_mode & FMODE_READ)) | ||
229 | goto out_fput; | ||
230 | |||
231 | /* src and dst must be regular files */ | ||
232 | ret = -EISDIR; | ||
233 | if (!S_ISREG(src_inode->i_mode) || !S_ISREG(dst_inode->i_mode)) | ||
234 | goto out_fput; | ||
235 | |||
236 | ret = -EXDEV; | ||
237 | if (src_file.file->f_path.mnt != dst_file->f_path.mnt || | ||
238 | src_inode->i_sb != dst_inode->i_sb) | ||
239 | goto out_fput; | ||
240 | |||
241 | /* XXX: do we lock at all? what if server needs CB_RECALL_LAYOUT? */ | ||
242 | if (dst_inode < src_inode) { | ||
243 | mutex_lock_nested(&dst_inode->i_mutex, I_MUTEX_PARENT); | ||
244 | mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_CHILD); | ||
245 | } else { | ||
246 | mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_PARENT); | ||
247 | mutex_lock_nested(&dst_inode->i_mutex, I_MUTEX_CHILD); | ||
248 | } | ||
249 | |||
250 | /* flush all pending writes on both src and dst so that server | ||
251 | * has the latest data */ | ||
252 | ret = nfs_sync_inode(src_inode); | ||
253 | if (ret) | ||
254 | goto out_unlock; | ||
255 | ret = nfs_sync_inode(dst_inode); | ||
256 | if (ret) | ||
257 | goto out_unlock; | ||
258 | |||
259 | ret = nfs42_proc_clone(src_file.file, dst_file, src_off, dst_off, count); | ||
260 | |||
261 | /* truncate inode page cache of the dst range so that future reads can fetch | ||
262 | * new data from server */ | ||
263 | if (!ret) | ||
264 | truncate_inode_pages_range(&dst_inode->i_data, dst_off, dst_off + count - 1); | ||
265 | |||
266 | out_unlock: | ||
267 | if (dst_inode < src_inode) { | ||
268 | mutex_unlock(&src_inode->i_mutex); | ||
269 | mutex_unlock(&dst_inode->i_mutex); | ||
270 | } else { | ||
271 | mutex_unlock(&dst_inode->i_mutex); | ||
272 | mutex_unlock(&src_inode->i_mutex); | ||
273 | } | ||
274 | out_fput: | ||
275 | fdput(src_file); | ||
276 | out_drop_write: | ||
277 | mnt_drop_write_file(dst_file); | ||
278 | return ret; | ||
279 | } | ||
195 | #endif /* CONFIG_NFS_V4_2 */ | 280 | #endif /* CONFIG_NFS_V4_2 */ |
196 | 281 | ||
282 | long nfs4_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
283 | { | ||
284 | switch (cmd) { | ||
285 | #ifdef CONFIG_NFS_V4_2 | ||
286 | case NFS_IOC_CLONE: | ||
287 | return nfs42_ioctl_clone(file, arg, 0, 0, 0); | ||
288 | #endif | ||
289 | } | ||
290 | |||
291 | return -ENOTTY; | ||
292 | } | ||
293 | |||
197 | const struct file_operations nfs4_file_operations = { | 294 | const struct file_operations nfs4_file_operations = { |
198 | #ifdef CONFIG_NFS_V4_2 | 295 | #ifdef CONFIG_NFS_V4_2 |
199 | .llseek = nfs4_file_llseek, | 296 | .llseek = nfs4_file_llseek, |
@@ -216,4 +313,9 @@ const struct file_operations nfs4_file_operations = { | |||
216 | #endif /* CONFIG_NFS_V4_2 */ | 313 | #endif /* CONFIG_NFS_V4_2 */ |
217 | .check_flags = nfs_check_flags, | 314 | .check_flags = nfs_check_flags, |
218 | .setlease = simple_nosetlease, | 315 | .setlease = simple_nosetlease, |
316 | #ifdef CONFIG_COMPAT | ||
317 | .unlocked_ioctl = nfs4_ioctl, | ||
318 | #else | ||
319 | .compat_ioctl = nfs4_ioctl, | ||
320 | #endif /* CONFIG_COMPAT */ | ||
219 | }; | 321 | }; |
diff --git a/include/uapi/linux/nfs.h b/include/uapi/linux/nfs.h index 5199a36dd574..d85748d3564d 100644 --- a/include/uapi/linux/nfs.h +++ b/include/uapi/linux/nfs.h | |||
@@ -31,6 +31,10 @@ | |||
31 | 31 | ||
32 | #define NFS_PIPE_DIRNAME "nfs" | 32 | #define NFS_PIPE_DIRNAME "nfs" |
33 | 33 | ||
34 | /* NFS ioctls */ | ||
35 | /* Let's follow btrfs lead on CLONE to avoid messing userspace */ | ||
36 | #define NFS_IOC_CLONE _IOW(0x94, 9, int) | ||
37 | |||
34 | /* | 38 | /* |
35 | * NFS stats. The good thing with these values is that NFSv3 errors are | 39 | * NFS stats. The good thing with these values is that NFSv3 errors are |
36 | * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which | 40 | * a superset of NFSv2 errors (with the exception of NFSERR_WFLUSH which |