diff options
Diffstat (limited to 'fs/nfs/nfs42proc.c')
-rw-r--r-- | fs/nfs/nfs42proc.c | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c index 0f020e4d8421..3e92a3cde15d 100644 --- a/fs/nfs/nfs42proc.c +++ b/fs/nfs/nfs42proc.c | |||
@@ -271,3 +271,74 @@ int nfs42_proc_layoutstats_generic(struct nfs_server *server, | |||
271 | return PTR_ERR(task); | 271 | return PTR_ERR(task); |
272 | return 0; | 272 | return 0; |
273 | } | 273 | } |
274 | |||
275 | static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f, | ||
276 | struct file *dst_f, loff_t src_offset, | ||
277 | loff_t dst_offset, loff_t count) | ||
278 | { | ||
279 | struct inode *src_inode = file_inode(src_f); | ||
280 | struct inode *dst_inode = file_inode(dst_f); | ||
281 | struct nfs_server *server = NFS_SERVER(dst_inode); | ||
282 | struct nfs42_clone_args args = { | ||
283 | .src_fh = NFS_FH(src_inode), | ||
284 | .dst_fh = NFS_FH(dst_inode), | ||
285 | .src_offset = src_offset, | ||
286 | .dst_offset = dst_offset, | ||
287 | .dst_bitmask = server->cache_consistency_bitmask, | ||
288 | }; | ||
289 | struct nfs42_clone_res res = { | ||
290 | .server = server, | ||
291 | }; | ||
292 | int status; | ||
293 | |||
294 | msg->rpc_argp = &args; | ||
295 | msg->rpc_resp = &res; | ||
296 | |||
297 | status = nfs42_set_rw_stateid(&args.src_stateid, src_f, FMODE_READ); | ||
298 | if (status) | ||
299 | return status; | ||
300 | |||
301 | status = nfs42_set_rw_stateid(&args.dst_stateid, dst_f, FMODE_WRITE); | ||
302 | if (status) | ||
303 | return status; | ||
304 | |||
305 | res.dst_fattr = nfs_alloc_fattr(); | ||
306 | if (!res.dst_fattr) | ||
307 | return -ENOMEM; | ||
308 | |||
309 | status = nfs4_call_sync(server->client, server, msg, | ||
310 | &args.seq_args, &res.seq_res, 0); | ||
311 | if (status == 0) | ||
312 | status = nfs_post_op_update_inode(dst_inode, res.dst_fattr); | ||
313 | |||
314 | kfree(res.dst_fattr); | ||
315 | return status; | ||
316 | } | ||
317 | |||
318 | int nfs42_proc_clone(struct file *src_f, struct file *dst_f, | ||
319 | loff_t src_offset, loff_t dst_offset, loff_t count) | ||
320 | { | ||
321 | struct rpc_message msg = { | ||
322 | .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLONE], | ||
323 | }; | ||
324 | struct inode *inode = file_inode(src_f); | ||
325 | struct nfs_server *server = NFS_SERVER(file_inode(src_f)); | ||
326 | struct nfs4_exception exception = { }; | ||
327 | int err; | ||
328 | |||
329 | if (!nfs_server_capable(inode, NFS_CAP_CLONE)) | ||
330 | return -EOPNOTSUPP; | ||
331 | |||
332 | do { | ||
333 | err = _nfs42_proc_clone(&msg, src_f, dst_f, src_offset, | ||
334 | dst_offset, count); | ||
335 | if (err == -ENOTSUPP || err == -EOPNOTSUPP) { | ||
336 | NFS_SERVER(inode)->caps &= ~NFS_CAP_CLONE; | ||
337 | return -EOPNOTSUPP; | ||
338 | } | ||
339 | err = nfs4_handle_exception(server, err, &exception); | ||
340 | } while (exception.retry); | ||
341 | |||
342 | return err; | ||
343 | |||
344 | } | ||