2014-09-26 21:58:48 +04:00
/*
* Copyright ( c ) 2014 Anna Schumaker < Anna . Schumaker @ Netapp . com >
*/
# include <linux/fs.h>
# include <linux/sunrpc/sched.h>
# include <linux/nfs.h>
# include <linux/nfs3.h>
# include <linux/nfs4.h>
# include <linux/nfs_xdr.h>
# include <linux/nfs_fs.h>
# include "nfs4_fs.h"
# include "nfs42.h"
2015-06-23 14:51:56 +03:00
# include "iostat.h"
# include "pnfs.h"
2017-01-09 23:14:33 +03:00
# include "nfs4session.h"
2015-06-23 14:51:56 +03:00
# include "internal.h"
2015-11-16 22:51:07 +03:00
# define NFSDBG_FACILITY NFSDBG_PROC
2014-09-26 21:58:48 +04:00
2014-11-25 21:18:15 +03:00
static int _nfs42_proc_fallocate ( struct rpc_message * msg , struct file * filep ,
2016-02-17 17:48:28 +03:00
struct nfs_lock_context * lock , loff_t offset , loff_t len )
2014-11-25 21:18:15 +03:00
{
struct inode * inode = file_inode ( filep ) ;
2015-03-16 21:06:23 +03:00
struct nfs_server * server = NFS_SERVER ( inode ) ;
2014-11-25 21:18:15 +03:00
struct nfs42_falloc_args args = {
. falloc_fh = NFS_FH ( inode ) ,
. falloc_offset = offset ,
. falloc_length = len ,
2015-03-16 21:06:23 +03:00
. falloc_bitmask = server - > cache_consistency_bitmask ,
} ;
struct nfs42_falloc_res res = {
. falloc_server = server ,
2014-11-25 21:18:15 +03:00
} ;
int status ;
msg - > rpc_argp = & args ;
msg - > rpc_resp = & res ;
2016-02-17 17:48:28 +03:00
status = nfs4_set_rw_stateid ( & args . falloc_stateid , lock - > open_context ,
lock , FMODE_WRITE ) ;
2014-11-25 21:18:15 +03:00
if ( status )
return status ;
2015-03-16 21:06:23 +03:00
res . falloc_fattr = nfs_alloc_fattr ( ) ;
if ( ! res . falloc_fattr )
return - ENOMEM ;
status = nfs4_call_sync ( server - > client , server , msg ,
& args . seq_args , & res . seq_res , 0 ) ;
if ( status = = 0 )
status = nfs_post_op_update_inode ( inode , res . falloc_fattr ) ;
kfree ( res . falloc_fattr ) ;
return status ;
2014-11-25 21:18:15 +03:00
}
static int nfs42_proc_fallocate ( struct rpc_message * msg , struct file * filep ,
loff_t offset , loff_t len )
{
struct nfs_server * server = NFS_SERVER ( file_inode ( filep ) ) ;
struct nfs4_exception exception = { } ;
2016-02-17 17:48:28 +03:00
struct nfs_lock_context * lock ;
2014-11-25 21:18:15 +03:00
int err ;
2016-02-17 17:48:28 +03:00
lock = nfs_get_lock_context ( nfs_file_open_context ( filep ) ) ;
if ( IS_ERR ( lock ) )
return PTR_ERR ( lock ) ;
exception . inode = file_inode ( filep ) ;
exception . state = lock - > open_context - > state ;
2014-11-25 21:18:15 +03:00
do {
2016-02-17 17:48:28 +03:00
err = _nfs42_proc_fallocate ( msg , filep , lock , offset , len ) ;
if ( err = = - ENOTSUPP ) {
err = - EOPNOTSUPP ;
break ;
}
2014-11-25 21:18:15 +03:00
err = nfs4_handle_exception ( server , err , & exception ) ;
} while ( exception . retry ) ;
2016-02-17 17:48:28 +03:00
nfs_put_lock_context ( lock ) ;
2014-11-25 21:18:15 +03:00
return err ;
}
int nfs42_proc_allocate ( struct file * filep , loff_t offset , loff_t len )
{
struct rpc_message msg = {
. rpc_proc = & nfs4_procedures [ NFSPROC4_CLNT_ALLOCATE ] ,
} ;
struct inode * inode = file_inode ( filep ) ;
int err ;
if ( ! nfs_server_capable ( inode , NFS_CAP_ALLOCATE ) )
return - EOPNOTSUPP ;
2016-01-22 23:40:57 +03:00
inode_lock ( inode ) ;
2015-03-16 21:06:24 +03:00
2014-11-25 21:18:15 +03:00
err = nfs42_proc_fallocate ( & msg , filep , offset , len ) ;
if ( err = = - EOPNOTSUPP )
NFS_SERVER ( inode ) - > caps & = ~ NFS_CAP_ALLOCATE ;
2015-03-16 21:06:24 +03:00
2016-01-22 23:40:57 +03:00
inode_unlock ( inode ) ;
2014-11-25 21:18:15 +03:00
return err ;
}
2014-11-25 21:18:16 +03:00
int nfs42_proc_deallocate ( struct file * filep , loff_t offset , loff_t len )
{
struct rpc_message msg = {
. rpc_proc = & nfs4_procedures [ NFSPROC4_CLNT_DEALLOCATE ] ,
} ;
struct inode * inode = file_inode ( filep ) ;
int err ;
if ( ! nfs_server_capable ( inode , NFS_CAP_DEALLOCATE ) )
return - EOPNOTSUPP ;
2016-01-22 23:40:57 +03:00
inode_lock ( inode ) ;
2016-06-26 00:50:53 +03:00
err = nfs_sync_inode ( inode ) ;
if ( err )
goto out_unlock ;
2015-03-16 21:06:24 +03:00
2014-11-25 21:18:16 +03:00
err = nfs42_proc_fallocate ( & msg , filep , offset , len ) ;
2015-03-16 21:06:23 +03:00
if ( err = = 0 )
truncate_pagecache_range ( inode , offset , ( offset + len ) - 1 ) ;
2014-11-25 21:18:16 +03:00
if ( err = = - EOPNOTSUPP )
NFS_SERVER ( inode ) - > caps & = ~ NFS_CAP_DEALLOCATE ;
2016-06-26 00:50:53 +03:00
out_unlock :
2016-01-22 23:40:57 +03:00
inode_unlock ( inode ) ;
2014-11-25 21:18:16 +03:00
return err ;
}
2017-02-18 02:42:32 +03:00
static ssize_t _nfs42_proc_copy ( struct file * src ,
2013-05-22 00:53:03 +04:00
struct nfs_lock_context * src_lock ,
2017-02-18 02:42:32 +03:00
struct file * dst ,
2013-05-22 00:53:03 +04:00
struct nfs_lock_context * dst_lock ,
2017-02-18 02:42:32 +03:00
struct nfs42_copy_args * args ,
struct nfs42_copy_res * res )
2013-05-22 00:53:03 +04:00
{
struct rpc_message msg = {
. rpc_proc = & nfs4_procedures [ NFSPROC4_CLNT_COPY ] ,
2017-02-18 02:42:32 +03:00
. rpc_argp = args ,
. rpc_resp = res ,
2013-05-22 00:53:03 +04:00
} ;
struct inode * dst_inode = file_inode ( dst ) ;
struct nfs_server * server = NFS_SERVER ( dst_inode ) ;
2017-02-18 02:42:32 +03:00
loff_t pos_src = args - > src_pos ;
loff_t pos_dst = args - > dst_pos ;
size_t count = args - > count ;
2013-05-22 00:53:03 +04:00
int status ;
2017-02-18 02:42:32 +03:00
status = nfs4_set_rw_stateid ( & args - > src_stateid , src_lock - > open_context ,
2013-05-22 00:53:03 +04:00
src_lock , FMODE_READ ) ;
if ( status )
return status ;
2016-06-26 01:12:03 +03:00
status = nfs_filemap_write_and_wait_range ( file_inode ( src ) - > i_mapping ,
pos_src , pos_src + ( loff_t ) count - 1 ) ;
if ( status )
return status ;
2017-02-18 02:42:32 +03:00
status = nfs4_set_rw_stateid ( & args - > dst_stateid , dst_lock - > open_context ,
2013-05-22 00:53:03 +04:00
dst_lock , FMODE_WRITE ) ;
if ( status )
return status ;
2016-06-26 01:12:03 +03:00
status = nfs_sync_inode ( dst_inode ) ;
if ( status )
return status ;
2013-05-22 00:53:03 +04:00
2017-05-09 01:02:24 +03:00
res - > commit_res . verf = kzalloc ( sizeof ( struct nfs_writeverf ) , GFP_NOFS ) ;
if ( ! res - > commit_res . verf )
return - ENOMEM ;
2013-05-22 00:53:03 +04:00
status = nfs4_call_sync ( server - > client , server , & msg ,
2017-02-18 02:42:32 +03:00
& args - > seq_args , & res - > seq_res , 0 ) ;
2013-05-22 00:53:03 +04:00
if ( status = = - ENOTSUPP )
server - > caps & = ~ NFS_CAP_COPY ;
if ( status )
2017-05-09 01:02:24 +03:00
goto out ;
2013-05-22 00:53:03 +04:00
2017-05-09 01:02:24 +03:00
if ( ! nfs_write_verifier_cmp ( & res - > write_res . verifier . verifier ,
& res - > commit_res . verf - > verifier ) ) {
status = - EAGAIN ;
goto out ;
2013-05-22 00:53:03 +04:00
}
truncate_pagecache_range ( dst_inode , pos_dst ,
2017-02-18 02:42:32 +03:00
pos_dst + res - > write_res . count ) ;
2013-05-22 00:53:03 +04:00
2017-05-09 01:02:24 +03:00
status = res - > write_res . count ;
out :
kfree ( res - > commit_res . verf ) ;
return status ;
2013-05-22 00:53:03 +04:00
}
ssize_t nfs42_proc_copy ( struct file * src , loff_t pos_src ,
struct file * dst , loff_t pos_dst ,
size_t count )
{
struct nfs_server * server = NFS_SERVER ( file_inode ( dst ) ) ;
struct nfs_lock_context * src_lock ;
struct nfs_lock_context * dst_lock ;
2017-02-18 02:42:32 +03:00
struct nfs42_copy_args args = {
. src_fh = NFS_FH ( file_inode ( src ) ) ,
. src_pos = pos_src ,
. dst_fh = NFS_FH ( file_inode ( dst ) ) ,
. dst_pos = pos_dst ,
. count = count ,
} ;
struct nfs42_copy_res res ;
struct nfs4_exception src_exception = {
. inode = file_inode ( src ) ,
. stateid = & args . src_stateid ,
} ;
struct nfs4_exception dst_exception = {
. inode = file_inode ( dst ) ,
. stateid = & args . dst_stateid ,
} ;
2013-05-22 00:53:03 +04:00
ssize_t err , err2 ;
if ( ! nfs_server_capable ( file_inode ( dst ) , NFS_CAP_COPY ) )
return - EOPNOTSUPP ;
src_lock = nfs_get_lock_context ( nfs_file_open_context ( src ) ) ;
if ( IS_ERR ( src_lock ) )
return PTR_ERR ( src_lock ) ;
src_exception . state = src_lock - > open_context - > state ;
dst_lock = nfs_get_lock_context ( nfs_file_open_context ( dst ) ) ;
if ( IS_ERR ( dst_lock ) ) {
err = PTR_ERR ( dst_lock ) ;
goto out_put_src_lock ;
}
dst_exception . state = dst_lock - > open_context - > state ;
do {
2016-05-26 20:33:33 +03:00
inode_lock ( file_inode ( dst ) ) ;
2017-02-18 02:42:32 +03:00
err = _nfs42_proc_copy ( src , src_lock ,
dst , dst_lock ,
& args , & res ) ;
2016-05-26 20:33:33 +03:00
inode_unlock ( file_inode ( dst ) ) ;
2013-05-22 00:53:03 +04:00
2017-02-18 02:42:32 +03:00
if ( err > = 0 )
break ;
2013-05-22 00:53:03 +04:00
if ( err = = - ENOTSUPP ) {
err = - EOPNOTSUPP ;
break ;
2017-05-09 01:02:24 +03:00
} if ( err = = - EAGAIN ) {
dst_exception . retry = 1 ;
continue ;
2013-05-22 00:53:03 +04:00
}
err2 = nfs4_handle_exception ( server , err , & src_exception ) ;
err = nfs4_handle_exception ( server , err , & dst_exception ) ;
if ( ! err )
err = err2 ;
} while ( src_exception . retry | | dst_exception . retry ) ;
nfs_put_lock_context ( dst_lock ) ;
out_put_src_lock :
nfs_put_lock_context ( src_lock ) ;
return err ;
}
2016-02-17 17:48:28 +03:00
static loff_t _nfs42_proc_llseek ( struct file * filep ,
struct nfs_lock_context * lock , loff_t offset , int whence )
2014-09-26 21:58:48 +04:00
{
struct inode * inode = file_inode ( filep ) ;
struct nfs42_seek_args args = {
. sa_fh = NFS_FH ( inode ) ,
. sa_offset = offset ,
. sa_what = ( whence = = SEEK_HOLE ) ?
NFS4_CONTENT_HOLE : NFS4_CONTENT_DATA ,
} ;
struct nfs42_seek_res res ;
struct rpc_message msg = {
. rpc_proc = & nfs4_procedures [ NFSPROC4_CLNT_SEEK ] ,
. rpc_argp = & args ,
. rpc_resp = & res ,
} ;
struct nfs_server * server = NFS_SERVER ( inode ) ;
int status ;
2014-10-23 22:00:54 +04:00
if ( ! nfs_server_capable ( inode , NFS_CAP_SEEK ) )
2014-09-26 21:58:48 +04:00
return - ENOTSUPP ;
2016-02-17 17:48:28 +03:00
status = nfs4_set_rw_stateid ( & args . sa_stateid , lock - > open_context ,
lock , FMODE_READ ) ;
2014-09-26 21:58:48 +04:00
if ( status )
return status ;
2016-06-26 00:57:39 +03:00
status = nfs_filemap_write_and_wait_range ( inode - > i_mapping ,
offset , LLONG_MAX ) ;
if ( status )
return status ;
2014-09-26 21:58:48 +04:00
status = nfs4_call_sync ( server - > client , server , & msg ,
& args . seq_args , & res . seq_res , 0 ) ;
if ( status = = - ENOTSUPP )
server - > caps & = ~ NFS_CAP_SEEK ;
if ( status )
return status ;
return vfs_setpos ( filep , res . sr_offset , inode - > i_sb - > s_maxbytes ) ;
}
2015-06-23 14:51:55 +03:00
2015-07-23 18:08:43 +03:00
loff_t nfs42_proc_llseek ( struct file * filep , loff_t offset , int whence )
{
struct nfs_server * server = NFS_SERVER ( file_inode ( filep ) ) ;
struct nfs4_exception exception = { } ;
2016-02-17 17:48:28 +03:00
struct nfs_lock_context * lock ;
2015-09-17 00:21:27 +03:00
loff_t err ;
2015-07-23 18:08:43 +03:00
2016-02-17 17:48:28 +03:00
lock = nfs_get_lock_context ( nfs_file_open_context ( filep ) ) ;
if ( IS_ERR ( lock ) )
return PTR_ERR ( lock ) ;
exception . inode = file_inode ( filep ) ;
exception . state = lock - > open_context - > state ;
2015-07-23 18:08:43 +03:00
do {
2016-02-17 17:48:28 +03:00
err = _nfs42_proc_llseek ( filep , lock , offset , whence ) ;
2015-09-17 00:21:27 +03:00
if ( err > = 0 )
break ;
2016-02-17 17:48:28 +03:00
if ( err = = - ENOTSUPP ) {
err = - EOPNOTSUPP ;
break ;
}
2015-07-23 18:08:43 +03:00
err = nfs4_handle_exception ( server , err , & exception ) ;
} while ( exception . retry ) ;
2016-02-17 17:48:28 +03:00
nfs_put_lock_context ( lock ) ;
2015-07-23 18:08:43 +03:00
return err ;
}
2015-06-23 14:51:56 +03:00
static void
nfs42_layoutstat_prepare ( struct rpc_task * task , void * calldata )
{
struct nfs42_layoutstat_data * data = calldata ;
2016-08-19 22:33:12 +03:00
struct inode * inode = data - > inode ;
struct nfs_server * server = NFS_SERVER ( inode ) ;
struct pnfs_layout_hdr * lo ;
2015-06-23 14:51:56 +03:00
2016-08-19 22:33:12 +03:00
spin_lock ( & inode - > i_lock ) ;
lo = NFS_I ( inode ) - > layout ;
if ( ! pnfs_layout_is_valid ( lo ) ) {
spin_unlock ( & inode - > i_lock ) ;
rpc_exit ( task , 0 ) ;
return ;
}
nfs4_stateid_copy ( & data - > args . stateid , & lo - > plh_stateid ) ;
spin_unlock ( & inode - > i_lock ) ;
2017-01-10 00:51:52 +03:00
nfs4_setup_sequence ( server - > nfs_client , & data - > args . seq_args ,
& data - > res . seq_res , task ) ;
2015-06-23 14:51:56 +03:00
}
static void
nfs42_layoutstat_done ( struct rpc_task * task , void * calldata )
{
struct nfs42_layoutstat_data * data = calldata ;
2015-12-07 04:55:09 +03:00
struct inode * inode = data - > inode ;
struct pnfs_layout_hdr * lo ;
2015-06-23 14:51:56 +03:00
if ( ! nfs4_sequence_done ( task , & data - > res . seq_res ) )
return ;
2015-06-27 18:45:46 +03:00
switch ( task - > tk_status ) {
case 0 :
break ;
2015-12-07 04:55:09 +03:00
case - NFS4ERR_EXPIRED :
2016-08-05 19:16:19 +03:00
case - NFS4ERR_ADMIN_REVOKED :
case - NFS4ERR_DELEG_REVOKED :
2015-12-07 04:55:09 +03:00
case - NFS4ERR_STALE_STATEID :
case - NFS4ERR_BAD_STATEID :
spin_lock ( & inode - > i_lock ) ;
lo = NFS_I ( inode ) - > layout ;
2016-08-19 22:33:12 +03:00
if ( pnfs_layout_is_valid ( lo ) & &
nfs4_stateid_match ( & data - > args . stateid ,
2015-12-07 04:55:09 +03:00
& lo - > plh_stateid ) ) {
LIST_HEAD ( head ) ;
/*
* Mark the bad layout state as invalid , then retry
* with the current stateid .
*/
2016-07-22 18:25:27 +03:00
pnfs_mark_layout_stateid_invalid ( lo , & head ) ;
2015-12-07 04:55:09 +03:00
spin_unlock ( & inode - > i_lock ) ;
pnfs_free_lseg_list ( & head ) ;
2017-04-29 17:10:17 +03:00
nfs_commit_inode ( inode , 0 ) ;
2015-12-07 04:55:09 +03:00
} else
spin_unlock ( & inode - > i_lock ) ;
break ;
2016-08-19 22:33:12 +03:00
case - NFS4ERR_OLD_STATEID :
spin_lock ( & inode - > i_lock ) ;
lo = NFS_I ( inode ) - > layout ;
if ( pnfs_layout_is_valid ( lo ) & &
nfs4_stateid_match_other ( & data - > args . stateid ,
& lo - > plh_stateid ) ) {
/* Do we need to delay before resending? */
if ( ! nfs4_stateid_is_newer ( & lo - > plh_stateid ,
& data - > args . stateid ) )
rpc_delay ( task , HZ ) ;
rpc_restart_call_prepare ( task ) ;
}
spin_unlock ( & inode - > i_lock ) ;
break ;
2015-06-27 18:45:46 +03:00
case - ENOTSUPP :
case - EOPNOTSUPP :
2015-12-07 04:55:09 +03:00
NFS_SERVER ( inode ) - > caps & = ~ NFS_CAP_LAYOUTSTATS ;
2015-06-27 18:45:46 +03:00
}
2015-06-23 14:51:56 +03:00
}
static void
nfs42_layoutstat_release ( void * calldata )
{
struct nfs42_layoutstat_data * data = calldata ;
2016-10-07 00:53:20 +03:00
struct nfs42_layoutstat_devinfo * devinfo = data - > args . devinfo ;
int i ;
2015-06-23 14:51:57 +03:00
2016-10-07 00:53:20 +03:00
for ( i = 0 ; i < data - > args . num_dev ; i + + ) {
if ( devinfo [ i ] . ld_private . ops & & devinfo [ i ] . ld_private . ops - > free )
devinfo [ i ] . ld_private . ops - > free ( & devinfo [ i ] . ld_private ) ;
}
2015-06-23 14:51:56 +03:00
pnfs_put_layout_hdr ( NFS_I ( data - > args . inode ) - > layout ) ;
2015-06-23 14:52:03 +03:00
smp_mb__before_atomic ( ) ;
clear_bit ( NFS_INO_LAYOUTSTATS , & NFS_I ( data - > args . inode ) - > flags ) ;
smp_mb__after_atomic ( ) ;
2015-06-23 14:51:56 +03:00
nfs_iput_and_deactive ( data - > inode ) ;
kfree ( data - > args . devinfo ) ;
kfree ( data ) ;
}
2015-06-23 14:51:55 +03:00
static const struct rpc_call_ops nfs42_layoutstat_ops = {
2015-06-23 14:51:56 +03:00
. rpc_call_prepare = nfs42_layoutstat_prepare ,
. rpc_call_done = nfs42_layoutstat_done ,
. rpc_release = nfs42_layoutstat_release ,
2015-06-23 14:51:55 +03:00
} ;
int nfs42_proc_layoutstats_generic ( struct nfs_server * server ,
struct nfs42_layoutstat_data * data )
{
struct rpc_message msg = {
. rpc_proc = & nfs4_procedures [ NFSPROC4_CLNT_LAYOUTSTATS ] ,
. rpc_argp = & data - > args ,
. rpc_resp = & data - > res ,
} ;
struct rpc_task_setup task_setup = {
. rpc_client = server - > client ,
. rpc_message = & msg ,
. callback_ops = & nfs42_layoutstat_ops ,
. callback_data = data ,
. flags = RPC_TASK_ASYNC ,
} ;
struct rpc_task * task ;
2015-06-23 14:51:56 +03:00
data - > inode = nfs_igrab_and_active ( data - > args . inode ) ;
if ( ! data - > inode ) {
nfs42_layoutstat_release ( data ) ;
return - EAGAIN ;
}
2015-06-23 14:51:55 +03:00
nfs4_init_sequence ( & data - > args . seq_args , & data - > res . seq_res , 0 ) ;
task = rpc_run_task ( & task_setup ) ;
if ( IS_ERR ( task ) )
return PTR_ERR ( task ) ;
2016-10-04 07:07:43 +03:00
rpc_put_task ( task ) ;
2015-06-23 14:51:55 +03:00
return 0 ;
}
2015-09-25 21:24:35 +03:00
static int _nfs42_proc_clone ( struct rpc_message * msg , struct file * src_f ,
2016-02-17 17:48:28 +03:00
struct file * dst_f , struct nfs_lock_context * src_lock ,
struct nfs_lock_context * dst_lock , loff_t src_offset ,
loff_t dst_offset , loff_t count )
2015-09-25 21:24:35 +03:00
{
struct inode * src_inode = file_inode ( src_f ) ;
struct inode * dst_inode = file_inode ( dst_f ) ;
struct nfs_server * server = NFS_SERVER ( dst_inode ) ;
struct nfs42_clone_args args = {
. src_fh = NFS_FH ( src_inode ) ,
. dst_fh = NFS_FH ( dst_inode ) ,
. src_offset = src_offset ,
. dst_offset = dst_offset ,
2015-11-13 11:38:45 +03:00
. count = count ,
2015-09-25 21:24:35 +03:00
. dst_bitmask = server - > cache_consistency_bitmask ,
} ;
struct nfs42_clone_res res = {
. server = server ,
} ;
int status ;
msg - > rpc_argp = & args ;
msg - > rpc_resp = & res ;
2016-02-17 17:48:28 +03:00
status = nfs4_set_rw_stateid ( & args . src_stateid , src_lock - > open_context ,
src_lock , FMODE_READ ) ;
2015-09-25 21:24:35 +03:00
if ( status )
return status ;
2016-02-17 17:48:28 +03:00
status = nfs4_set_rw_stateid ( & args . dst_stateid , dst_lock - > open_context ,
dst_lock , FMODE_WRITE ) ;
2015-09-25 21:24:35 +03:00
if ( status )
return status ;
res . dst_fattr = nfs_alloc_fattr ( ) ;
if ( ! res . dst_fattr )
return - ENOMEM ;
status = nfs4_call_sync ( server - > client , server , msg ,
& args . seq_args , & res . seq_res , 0 ) ;
if ( status = = 0 )
status = nfs_post_op_update_inode ( dst_inode , res . dst_fattr ) ;
kfree ( res . dst_fattr ) ;
return status ;
}
int nfs42_proc_clone ( struct file * src_f , struct file * dst_f ,
loff_t src_offset , loff_t dst_offset , loff_t count )
{
struct rpc_message msg = {
. rpc_proc = & nfs4_procedures [ NFSPROC4_CLNT_CLONE ] ,
} ;
struct inode * inode = file_inode ( src_f ) ;
struct nfs_server * server = NFS_SERVER ( file_inode ( src_f ) ) ;
2016-02-17 17:48:28 +03:00
struct nfs_lock_context * src_lock ;
struct nfs_lock_context * dst_lock ;
struct nfs4_exception src_exception = { } ;
struct nfs4_exception dst_exception = { } ;
int err , err2 ;
2015-09-25 21:24:35 +03:00
if ( ! nfs_server_capable ( inode , NFS_CAP_CLONE ) )
return - EOPNOTSUPP ;
2016-02-17 17:48:28 +03:00
src_lock = nfs_get_lock_context ( nfs_file_open_context ( src_f ) ) ;
if ( IS_ERR ( src_lock ) )
return PTR_ERR ( src_lock ) ;
src_exception . inode = file_inode ( src_f ) ;
src_exception . state = src_lock - > open_context - > state ;
dst_lock = nfs_get_lock_context ( nfs_file_open_context ( dst_f ) ) ;
if ( IS_ERR ( dst_lock ) ) {
err = PTR_ERR ( dst_lock ) ;
goto out_put_src_lock ;
}
dst_exception . inode = file_inode ( dst_f ) ;
dst_exception . state = dst_lock - > open_context - > state ;
2015-09-25 21:24:35 +03:00
do {
2016-02-17 17:48:28 +03:00
err = _nfs42_proc_clone ( & msg , src_f , dst_f , src_lock , dst_lock ,
src_offset , dst_offset , count ) ;
2015-09-25 21:24:35 +03:00
if ( err = = - ENOTSUPP | | err = = - EOPNOTSUPP ) {
NFS_SERVER ( inode ) - > caps & = ~ NFS_CAP_CLONE ;
2016-02-17 17:48:28 +03:00
err = - EOPNOTSUPP ;
break ;
2015-09-25 21:24:35 +03:00
}
2016-02-17 17:48:28 +03:00
err2 = nfs4_handle_exception ( server , err , & src_exception ) ;
err = nfs4_handle_exception ( server , err , & dst_exception ) ;
if ( ! err )
err = err2 ;
} while ( src_exception . retry | | dst_exception . retry ) ;
2015-09-25 21:24:35 +03:00
2016-02-17 17:48:28 +03:00
nfs_put_lock_context ( dst_lock ) ;
out_put_src_lock :
nfs_put_lock_context ( src_lock ) ;
return err ;
2015-09-25 21:24:35 +03:00
}