2005-04-16 15:20:36 -07:00
/*
* linux / fs / nfs / callback_proc . c
*
* Copyright ( C ) 2004 Trond Myklebust
*
* NFSv4 callback procedures
*/
# include <linux/nfs4.h>
# include <linux/nfs_fs.h>
2005-06-22 17:16:21 +00:00
# include "nfs4_fs.h"
2005-04-16 15:20:36 -07:00
# include "callback.h"
# include "delegation.h"
2006-08-22 20:06:10 -04:00
# include "internal.h"
2005-04-16 15:20:36 -07:00
2007-12-10 14:57:23 -05:00
# ifdef NFS_DEBUG
2005-04-16 15:20:36 -07:00
# define NFSDBG_FACILITY NFSDBG_CALLBACK
2007-12-10 14:57:23 -05:00
# endif
2005-04-16 15:20:36 -07:00
2006-10-19 23:28:50 -07:00
__be32 nfs4_callback_getattr ( struct cb_getattrargs * args , struct cb_getattrres * res )
2005-04-16 15:20:36 -07:00
{
2006-08-22 20:06:08 -04:00
struct nfs_client * clp ;
2005-04-16 15:20:36 -07:00
struct nfs_delegation * delegation ;
struct nfs_inode * nfsi ;
struct inode * inode ;
2007-12-10 14:57:23 -05:00
2005-04-16 15:20:36 -07:00
res - > bitmap [ 0 ] = res - > bitmap [ 1 ] = 0 ;
res - > status = htonl ( NFS4ERR_BADHANDLE ) ;
2007-12-10 14:58:44 -05:00
clp = nfs_find_client ( args - > addr , 4 ) ;
2005-04-16 15:20:36 -07:00
if ( clp = = NULL )
goto out ;
2007-12-10 14:57:23 -05:00
dprintk ( " NFS: GETATTR callback request from %s \n " ,
rpc_peeraddr2str ( clp - > cl_rpcclient , RPC_DISPLAY_ADDR ) ) ;
2005-04-16 15:20:36 -07:00
inode = nfs_delegation_find_inode ( clp , & args - > fh ) ;
if ( inode = = NULL )
goto out_putclient ;
nfsi = NFS_I ( inode ) ;
down_read ( & nfsi - > rwsem ) ;
delegation = nfsi - > delegation ;
if ( delegation = = NULL | | ( delegation - > type & FMODE_WRITE ) = = 0 )
goto out_iput ;
res - > size = i_size_read ( inode ) ;
2006-01-03 09:55:37 +01:00
res - > change_attr = delegation - > change_attr ;
if ( nfsi - > npages ! = 0 )
res - > change_attr + + ;
2005-04-16 15:20:36 -07:00
res - > ctime = inode - > i_ctime ;
res - > mtime = inode - > i_mtime ;
res - > bitmap [ 0 ] = ( FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE ) &
args - > bitmap [ 0 ] ;
res - > bitmap [ 1 ] = ( FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY ) &
args - > bitmap [ 1 ] ;
res - > status = 0 ;
out_iput :
up_read ( & nfsi - > rwsem ) ;
iput ( inode ) ;
out_putclient :
2006-08-22 20:06:10 -04:00
nfs_put_client ( clp ) ;
2005-04-16 15:20:36 -07:00
out :
2008-05-02 13:42:44 -07:00
dprintk ( " %s: exit with status = %d \n " , __func__ , ntohl ( res - > status ) ) ;
2005-04-16 15:20:36 -07:00
return res - > status ;
}
2006-10-19 23:28:50 -07:00
__be32 nfs4_callback_recall ( struct cb_recallargs * args , void * dummy )
2005-04-16 15:20:36 -07:00
{
2006-08-22 20:06:08 -04:00
struct nfs_client * clp ;
2005-04-16 15:20:36 -07:00
struct inode * inode ;
2006-10-19 23:28:50 -07:00
__be32 res ;
2005-04-16 15:20:36 -07:00
res = htonl ( NFS4ERR_BADHANDLE ) ;
2007-12-10 14:58:44 -05:00
clp = nfs_find_client ( args - > addr , 4 ) ;
2005-04-16 15:20:36 -07:00
if ( clp = = NULL )
goto out ;
2007-12-10 14:57:23 -05:00
dprintk ( " NFS: RECALL callback request from %s \n " ,
rpc_peeraddr2str ( clp - > cl_rpcclient , RPC_DISPLAY_ADDR ) ) ;
2008-01-26 01:06:40 -05:00
do {
struct nfs_client * prev = clp ;
inode = nfs_delegation_find_inode ( clp , & args - > fh ) ;
if ( inode ! = NULL ) {
/* Set up a helper thread to actually return the delegation */
switch ( nfs_async_inode_return_delegation ( inode , & args - > stateid ) ) {
case 0 :
res = 0 ;
break ;
case - ENOENT :
if ( res ! = 0 )
res = htonl ( NFS4ERR_BAD_STATEID ) ;
break ;
default :
res = htonl ( NFS4ERR_RESOURCE ) ;
}
iput ( inode ) ;
}
clp = nfs_find_client_next ( prev ) ;
nfs_put_client ( prev ) ;
} while ( clp ! = NULL ) ;
2005-04-16 15:20:36 -07:00
out :
2008-05-02 13:42:44 -07:00
dprintk ( " %s: exit with status = %d \n " , __func__ , ntohl ( res ) ) ;
2005-04-16 15:20:36 -07:00
return res ;
}
2009-04-01 09:23:25 -04:00
# if defined(CONFIG_NFS_V4_1)
2009-04-01 09:23:35 -04:00
/*
* Validate the sequenceID sent by the server .
* Return success if the sequenceID is one more than what we last saw on
* this slot , accounting for wraparound . Increments the slot ' s sequence .
*
* We don ' t yet implement a duplicate request cache , so at this time
* we will log replays , and process them as if we had not seen them before ,
* but we don ' t bump the sequence in the slot . Not too worried about it ,
* since we only currently implement idempotent callbacks anyway .
*
* We have a single slot backchannel at this time , so we don ' t bother
* checking the used_slots bit array on the table . The lower layer guarantees
* a single outstanding callback request at a time .
*/
static int
validate_seqid ( struct nfs4_slot_table * tbl , u32 slotid , u32 seqid )
{
struct nfs4_slot * slot ;
dprintk ( " %s enter. slotid %d seqid %d \n " ,
__func__ , slotid , seqid ) ;
if ( slotid > NFS41_BC_MAX_CALLBACKS )
return htonl ( NFS4ERR_BADSLOT ) ;
slot = tbl - > slots + slotid ;
dprintk ( " %s slot table seqid: %d \n " , __func__ , slot - > seq_nr ) ;
/* Normal */
if ( likely ( seqid = = slot - > seq_nr + 1 ) ) {
slot - > seq_nr + + ;
return htonl ( NFS4_OK ) ;
}
/* Replay */
if ( seqid = = slot - > seq_nr ) {
dprintk ( " %s seqid %d is a replay - no DRC available \n " ,
__func__ , seqid ) ;
return htonl ( NFS4_OK ) ;
}
/* Wraparound */
if ( seqid = = 1 & & ( slot - > seq_nr + 1 ) = = 0 ) {
slot - > seq_nr = 1 ;
return htonl ( NFS4_OK ) ;
}
/* Misordered request */
return htonl ( NFS4ERR_SEQ_MISORDERED ) ;
}
2009-04-01 09:23:34 -04:00
/*
* Returns a pointer to a held ' struct nfs_client ' that matches the server ' s
* address , major version number , and session ID . It is the caller ' s
* responsibility to release the returned reference .
*
* Returns NULL if there are no connections with sessions , or if no session
* matches the one of interest .
*/
static struct nfs_client * find_client_with_session (
const struct sockaddr * addr , u32 nfsversion ,
struct nfs4_sessionid * sessionid )
{
struct nfs_client * clp ;
clp = nfs_find_client ( addr , 4 ) ;
if ( clp = = NULL )
return NULL ;
do {
struct nfs_client * prev = clp ;
if ( clp - > cl_session ! = NULL ) {
if ( memcmp ( clp - > cl_session - > sess_id . data ,
sessionid - > data ,
NFS4_MAX_SESSIONID_LEN ) = = 0 ) {
/* Returns a held reference to clp */
return clp ;
}
}
clp = nfs_find_client_next ( prev ) ;
nfs_put_client ( prev ) ;
} while ( clp ! = NULL ) ;
return NULL ;
}
2009-04-01 09:23:25 -04:00
/* FIXME: referring calls should be processed */
unsigned nfs4_callback_sequence ( struct cb_sequenceargs * args ,
struct cb_sequenceres * res )
{
2009-04-01 09:23:35 -04:00
struct nfs_client * clp ;
int i , status ;
2009-04-01 09:23:25 -04:00
for ( i = 0 ; i < args - > csa_nrclists ; i + + )
kfree ( args - > csa_rclists [ i ] . rcl_refcalls ) ;
kfree ( args - > csa_rclists ) ;
2009-04-01 09:23:35 -04:00
status = htonl ( NFS4ERR_BADSESSION ) ;
clp = find_client_with_session ( args - > csa_addr , 4 , & args - > csa_sessionid ) ;
if ( clp = = NULL )
goto out ;
status = validate_seqid ( & clp - > cl_session - > bc_slot_table ,
args - > csa_slotid , args - > csa_sequenceid ) ;
if ( status )
goto out_putclient ;
2009-04-01 09:23:25 -04:00
memcpy ( & res - > csr_sessionid , & args - > csa_sessionid ,
sizeof ( res - > csr_sessionid ) ) ;
res - > csr_sequenceid = args - > csa_sequenceid ;
res - > csr_slotid = args - > csa_slotid ;
res - > csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1 ;
res - > csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1 ;
2009-04-01 09:23:35 -04:00
out_putclient :
nfs_put_client ( clp ) ;
out :
2009-04-01 09:23:25 -04:00
dprintk ( " %s: exit with status = %d \n " , __func__ , ntohl ( status ) ) ;
res - > csr_status = status ;
2009-04-01 09:23:35 -04:00
return res - > csr_status ;
2009-04-01 09:23:25 -04:00
}
# endif /* CONFIG_NFS_V4_1 */