aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeng Tao <tao.peng@primarydata.com>2015-09-25 14:24:36 -0400
committerTrond Myklebust <trond.myklebust@primarydata.com>2015-10-15 16:08:04 -0400
commitbea51b30b281039f0f43fb4f42028ddf33fb601f (patch)
treecbe3f34a5758b920289b5383ff72e2108b9fa070
parente5341f3a5762d17be9cdd06257c02c0098bdcab8 (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.c102
-rw-r--r--include/uapi/linux/nfs.h4
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
197static noinline long
198nfs42_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
266out_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 }
274out_fput:
275 fdput(src_file);
276out_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
282long 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
197const struct file_operations nfs4_file_operations = { 294const 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