2005-04-17 02:20:36 +04:00
/*
* fs / cifs / transport . c
*
2008-02-08 02:25:02 +03:00
* Copyright ( C ) International Business Machines Corp . , 2002 , 2008
2005-04-17 02:20:36 +04:00
* Author ( s ) : Steve French ( sfrench @ us . ibm . com )
2006-07-16 08:32:51 +04:00
* Jeremy Allison ( jra @ samba . org ) 2006.
2007-07-07 02:44:50 +04:00
*
2005-04-17 02:20:36 +04:00
* This library is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation ; either version 2.1 of the License , or
* ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See
* the GNU Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library ; if not , write to the Free Software
2007-07-07 02:44:50 +04:00
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2005-04-17 02:20:36 +04:00
*/
# include <linux/fs.h>
# include <linux/list.h>
# include <linux/wait.h>
# include <linux/net.h>
# include <linux/delay.h>
# include <asm/uaccess.h>
# include <asm/processor.h>
# include <linux/mempool.h>
# include "cifspdu.h"
# include "cifsglob.h"
# include "cifsproto.h"
# include "cifs_debug.h"
2007-07-13 04:33:32 +04:00
2005-04-17 02:20:36 +04:00
extern mempool_t * cifs_mid_poolp ;
2006-12-07 07:33:20 +03:00
extern struct kmem_cache * cifs_oplock_cachep ;
2005-04-17 02:20:36 +04:00
static struct mid_q_entry *
2008-12-01 15:09:34 +03:00
AllocMidQEntry ( const struct smb_hdr * smb_buffer , struct TCP_Server_Info * server )
2005-04-17 02:20:36 +04:00
{
struct mid_q_entry * temp ;
2008-12-01 15:09:34 +03:00
if ( server = = NULL ) {
2005-04-17 02:20:36 +04:00
cERROR ( 1 , ( " Null TCP session in AllocMidQEntry " ) ) ;
return NULL ;
}
2007-07-13 04:33:32 +04:00
2008-09-15 14:22:54 +04:00
temp = mempool_alloc ( cifs_mid_poolp , GFP_NOFS ) ;
2005-04-17 02:20:36 +04:00
if ( temp = = NULL )
return temp ;
else {
2007-08-31 02:09:15 +04:00
memset ( temp , 0 , sizeof ( struct mid_q_entry ) ) ;
2005-04-17 02:20:36 +04:00
temp - > mid = smb_buffer - > Mid ; /* always LE */
temp - > pid = current - > pid ;
temp - > command = smb_buffer - > Command ;
cFYI ( 1 , ( " For smb_command %d " , temp - > command ) ) ;
2005-10-12 06:58:06 +04:00
/* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */
/* when mid allocated can be before when sent */
temp - > when_alloc = jiffies ;
2005-04-17 02:20:36 +04:00
temp - > tsk = current ;
}
spin_lock ( & GlobalMid_Lock ) ;
2008-12-01 15:09:34 +03:00
list_add_tail ( & temp - > qhead , & server - > pending_mid_q ) ;
2005-04-17 02:20:36 +04:00
atomic_inc ( & midCount ) ;
temp - > midState = MID_REQUEST_ALLOCATED ;
spin_unlock ( & GlobalMid_Lock ) ;
return temp ;
}
static void
DeleteMidQEntry ( struct mid_q_entry * midEntry )
{
2005-10-12 06:58:06 +04:00
# ifdef CONFIG_CIFS_STATS2
unsigned long now ;
# endif
2005-04-17 02:20:36 +04:00
spin_lock ( & GlobalMid_Lock ) ;
midEntry - > midState = MID_FREE ;
list_del ( & midEntry - > qhead ) ;
atomic_dec ( & midCount ) ;
spin_unlock ( & GlobalMid_Lock ) ;
2007-07-07 02:44:50 +04:00
if ( midEntry - > largeBuf )
2005-04-29 09:41:07 +04:00
cifs_buf_release ( midEntry - > resp_buf ) ;
else
cifs_small_buf_release ( midEntry - > resp_buf ) ;
2005-10-12 06:58:06 +04:00
# ifdef CONFIG_CIFS_STATS2
now = jiffies ;
/* commands taking longer than one second are indications that
something is wrong , unless it is quite a slow link or server */
2007-07-07 02:44:50 +04:00
if ( ( now - midEntry - > when_alloc ) > HZ ) {
if ( ( cifsFYI & CIFS_TIMER ) & &
2005-10-12 06:58:06 +04:00
( midEntry - > command ! = SMB_COM_LOCKING_ANDX ) ) {
printk ( KERN_DEBUG " CIFS slow rsp: cmd %d mid %d " ,
midEntry - > command , midEntry - > mid ) ;
printk ( " A: 0x%lx S: 0x%lx R: 0x%lx \n " ,
now - midEntry - > when_alloc ,
now - midEntry - > when_sent ,
now - midEntry - > when_received ) ;
}
}
# endif
2005-04-17 02:20:36 +04:00
mempool_free ( midEntry , cifs_mid_poolp ) ;
}
struct oplock_q_entry *
2007-07-07 02:44:50 +04:00
AllocOplockQEntry ( struct inode * pinode , __u16 fid , struct cifsTconInfo * tcon )
2005-04-17 02:20:36 +04:00
{
struct oplock_q_entry * temp ;
2007-07-07 02:44:50 +04:00
if ( ( pinode = = NULL ) | | ( tcon = = NULL ) ) {
2005-04-17 02:20:36 +04:00
cERROR ( 1 , ( " Null parms passed to AllocOplockQEntry " ) ) ;
return NULL ;
}
temp = ( struct oplock_q_entry * ) kmem_cache_alloc ( cifs_oplock_cachep ,
2006-12-07 07:33:17 +03:00
GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( temp = = NULL )
return temp ;
else {
temp - > pinode = pinode ;
temp - > tcon = tcon ;
temp - > netfid = fid ;
spin_lock ( & GlobalMid_Lock ) ;
list_add_tail ( & temp - > qhead , & GlobalOplock_Q ) ;
spin_unlock ( & GlobalMid_Lock ) ;
}
return temp ;
}
2007-07-07 02:44:50 +04:00
void DeleteOplockQEntry ( struct oplock_q_entry * oplockEntry )
2005-04-17 02:20:36 +04:00
{
2007-07-07 02:44:50 +04:00
spin_lock ( & GlobalMid_Lock ) ;
2005-04-17 02:20:36 +04:00
/* should we check if list empty first? */
list_del ( & oplockEntry - > qhead ) ;
spin_unlock ( & GlobalMid_Lock ) ;
kmem_cache_free ( cifs_oplock_cachep , oplockEntry ) ;
}
2008-04-15 22:40:48 +04:00
void DeleteTconOplockQEntries ( struct cifsTconInfo * tcon )
{
struct oplock_q_entry * temp ;
if ( tcon = = NULL )
return ;
spin_lock ( & GlobalMid_Lock ) ;
list_for_each_entry ( temp , & GlobalOplock_Q , qhead ) {
if ( ( temp - > tcon ) & & ( temp - > tcon = = tcon ) ) {
list_del ( & temp - > qhead ) ;
kmem_cache_free ( cifs_oplock_cachep , temp ) ;
}
}
spin_unlock ( & GlobalMid_Lock ) ;
}
2005-04-17 02:20:36 +04:00
int
smb_send ( struct socket * ssocket , struct smb_hdr * smb_buffer ,
2008-10-29 03:47:57 +03:00
unsigned int smb_buf_length , struct sockaddr * sin , bool noblocksnd )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
int i = 0 ;
struct msghdr smb_msg ;
struct kvec iov ;
unsigned len = smb_buf_length + 4 ;
2007-07-07 02:44:50 +04:00
if ( ssocket = = NULL )
2005-04-17 02:20:36 +04:00
return - ENOTSOCK ; /* BB eventually add reconnect code here */
iov . iov_base = smb_buffer ;
iov . iov_len = len ;
smb_msg . msg_name = sin ;
2007-08-31 02:09:15 +04:00
smb_msg . msg_namelen = sizeof ( struct sockaddr ) ;
2005-04-17 02:20:36 +04:00
smb_msg . msg_control = NULL ;
smb_msg . msg_controllen = 0 ;
2008-10-29 03:47:57 +03:00
if ( noblocksnd )
smb_msg . msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL ;
else
smb_msg . msg_flags = MSG_NOSIGNAL ;
2005-04-17 02:20:36 +04:00
/* smb header is converted in header_assemble. bcc and rest of SMB word
2007-07-07 02:44:50 +04:00
area , and byte area if necessary , is converted to littleendian in
cifssmb . c and RFC1001 len is converted to bigendian in smb_send
2005-04-17 02:20:36 +04:00
Flags2 is converted in SendReceive */
smb_buffer - > smb_buf_length = cpu_to_be32 ( smb_buffer - > smb_buf_length ) ;
2005-10-04 00:37:24 +04:00
cFYI ( 1 , ( " Sending smb of length %d " , smb_buf_length ) ) ;
2005-04-17 02:20:36 +04:00
dump_smb ( smb_buffer , len ) ;
while ( len > 0 ) {
rc = kernel_sendmsg ( ssocket , & smb_msg , & iov , 1 , len ) ;
if ( ( rc = = - ENOSPC ) | | ( rc = = - EAGAIN ) ) {
i + + ;
2005-10-04 00:37:24 +04:00
/* smaller timeout here than send2 since smaller size */
2007-07-07 02:44:50 +04:00
/* Although it may not be required, this also is smaller
oplock break time */
if ( i > 12 ) {
2005-04-17 02:20:36 +04:00
cERROR ( 1 ,
2005-10-10 21:34:22 +04:00
( " sends on sock %p stuck for 7 seconds " ,
2005-04-17 02:20:36 +04:00
ssocket ) ) ;
rc = - EAGAIN ;
break ;
}
2005-10-10 21:34:22 +04:00
msleep ( 1 < < i ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
2007-07-07 02:44:50 +04:00
if ( rc < 0 )
2005-04-17 02:20:36 +04:00
break ;
2005-10-11 01:06:37 +04:00
else
i = 0 ; /* reset i after each successful send */
2005-04-17 02:20:36 +04:00
iov . iov_base + = rc ;
iov . iov_len - = rc ;
len - = rc ;
}
if ( rc < 0 ) {
2007-07-07 02:44:50 +04:00
cERROR ( 1 , ( " Error %d sending data on socket to server " , rc ) ) ;
2005-04-17 02:20:36 +04:00
} else {
rc = 0 ;
}
2006-08-03 01:56:33 +04:00
/* Don't want to modify the buffer as a
side effect of this call . */
smb_buffer - > smb_buf_length = smb_buf_length ;
2005-04-17 02:20:36 +04:00
return rc ;
}
2005-06-13 22:24:43 +04:00
static int
2008-10-29 03:47:57 +03:00
smb_send2 ( struct TCP_Server_Info * server , struct kvec * iov , int n_vec ,
struct sockaddr * sin , bool noblocksnd )
2005-04-17 02:20:36 +04:00
{
int rc = 0 ;
int i = 0 ;
struct msghdr smb_msg ;
2005-10-04 00:37:24 +04:00
struct smb_hdr * smb_buffer = iov [ 0 ] . iov_base ;
unsigned int len = iov [ 0 ] . iov_len ;
unsigned int total_len ;
int first_vec = 0 ;
2006-08-03 01:56:33 +04:00
unsigned int smb_buf_length = smb_buffer - > smb_buf_length ;
2008-10-29 03:47:57 +03:00
struct socket * ssocket = server - > ssocket ;
2007-07-13 04:33:32 +04:00
2007-07-07 02:44:50 +04:00
if ( ssocket = = NULL )
2005-04-17 02:20:36 +04:00
return - ENOTSOCK ; /* BB eventually add reconnect code here */
2005-10-04 00:37:24 +04:00
2005-04-17 02:20:36 +04:00
smb_msg . msg_name = sin ;
2007-08-31 02:09:15 +04:00
smb_msg . msg_namelen = sizeof ( struct sockaddr ) ;
2005-04-17 02:20:36 +04:00
smb_msg . msg_control = NULL ;
smb_msg . msg_controllen = 0 ;
2008-10-29 03:47:57 +03:00
if ( noblocksnd )
smb_msg . msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL ;
else
smb_msg . msg_flags = MSG_NOSIGNAL ;
2005-04-17 02:20:36 +04:00
/* smb header is converted in header_assemble. bcc and rest of SMB word
2007-07-07 02:44:50 +04:00
area , and byte area if necessary , is converted to littleendian in
cifssmb . c and RFC1001 len is converted to bigendian in smb_send
2005-04-17 02:20:36 +04:00
Flags2 is converted in SendReceive */
2005-10-04 00:37:24 +04:00
total_len = 0 ;
for ( i = 0 ; i < n_vec ; i + + )
total_len + = iov [ i ] . iov_len ;
2005-04-17 02:20:36 +04:00
smb_buffer - > smb_buf_length = cpu_to_be32 ( smb_buffer - > smb_buf_length ) ;
2005-10-04 00:37:24 +04:00
cFYI ( 1 , ( " Sending smb: total_len %d " , total_len ) ) ;
2005-04-17 02:20:36 +04:00
dump_smb ( smb_buffer , len ) ;
2008-07-30 01:26:13 +04:00
i = 0 ;
2005-10-04 00:37:24 +04:00
while ( total_len ) {
rc = kernel_sendmsg ( ssocket , & smb_msg , & iov [ first_vec ] ,
n_vec - first_vec , total_len ) ;
2005-04-17 02:20:36 +04:00
if ( ( rc = = - ENOSPC ) | | ( rc = = - EAGAIN ) ) {
i + + ;
2007-07-07 02:44:50 +04:00
if ( i > = 14 ) {
2005-04-17 02:20:36 +04:00
cERROR ( 1 ,
2005-10-10 21:34:22 +04:00
( " sends on sock %p stuck for 15 seconds " ,
2005-04-17 02:20:36 +04:00
ssocket ) ) ;
rc = - EAGAIN ;
break ;
}
2005-10-10 21:34:22 +04:00
msleep ( 1 < < i ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
2007-07-07 02:44:50 +04:00
if ( rc < 0 )
2005-04-17 02:20:36 +04:00
break ;
2005-10-04 00:37:24 +04:00
2008-10-30 23:15:22 +03:00
if ( rc = = total_len ) {
total_len = 0 ;
break ;
} else if ( rc > total_len ) {
cERROR ( 1 , ( " sent %d requested %d " , rc , total_len ) ) ;
2005-10-04 00:37:24 +04:00
break ;
}
2007-07-07 02:44:50 +04:00
if ( rc = = 0 ) {
2005-10-04 00:37:24 +04:00
/* should never happen, letting socket clear before
retrying is our only obvious option here */
2007-07-07 02:44:50 +04:00
cERROR ( 1 , ( " tcp sent no data " ) ) ;
2005-10-04 00:37:24 +04:00
msleep ( 500 ) ;
continue ;
2005-06-13 22:24:43 +04:00
}
2005-10-04 00:37:24 +04:00
total_len - = rc ;
2005-10-10 21:34:22 +04:00
/* the line below resets i */
2005-10-04 00:37:24 +04:00
for ( i = first_vec ; i < n_vec ; i + + ) {
if ( iov [ i ] . iov_len ) {
if ( rc > iov [ i ] . iov_len ) {
rc - = iov [ i ] . iov_len ;
iov [ i ] . iov_len = 0 ;
} else {
iov [ i ] . iov_base + = rc ;
iov [ i ] . iov_len - = rc ;
first_vec = i ;
break ;
}
}
2005-06-13 22:24:43 +04:00
}
2005-10-11 01:06:37 +04:00
i = 0 ; /* in case we get ENOSPC on the next send */
2005-04-17 02:20:36 +04:00
}
2008-10-29 03:47:57 +03:00
if ( ( total_len > 0 ) & & ( total_len ! = smb_buf_length + 4 ) ) {
cFYI ( 1 , ( " partial send (%d remaining), terminating session " ,
total_len ) ) ;
/* If we have only sent part of an SMB then the next SMB
could be taken as the remainder of this one . We need
to kill the socket so the server throws away the partial
SMB */
server - > tcpStatus = CifsNeedReconnect ;
}
2005-04-17 02:20:36 +04:00
if ( rc < 0 ) {
2007-07-07 02:44:50 +04:00
cERROR ( 1 , ( " Error %d sending data on socket to server " , rc ) ) ;
2005-10-04 00:37:24 +04:00
} else
2005-04-17 02:20:36 +04:00
rc = 0 ;
2006-08-03 01:56:33 +04:00
/* Don't want to modify the buffer as a
side effect of this call . */
smb_buffer - > smb_buf_length = smb_buf_length ;
2005-04-17 02:20:36 +04:00
return rc ;
}
2006-08-03 01:56:33 +04:00
static int wait_for_free_request ( struct cifsSesInfo * ses , const int long_op )
2005-04-17 02:20:36 +04:00
{
2007-11-14 01:41:37 +03:00
if ( long_op = = CIFS_ASYNC_OP ) {
2005-04-17 02:20:36 +04:00
/* oplock breaks must not be held up */
atomic_inc ( & ses - > server - > inFlight ) ;
} else {
2007-07-07 02:44:50 +04:00
spin_lock ( & GlobalMid_Lock ) ;
while ( 1 ) {
if ( atomic_read ( & ses - > server - > inFlight ) > =
2005-06-13 22:24:43 +04:00
cifs_max_pending ) {
2005-04-17 02:20:36 +04:00
spin_unlock ( & GlobalMid_Lock ) ;
2005-10-07 20:51:05 +04:00
# ifdef CONFIG_CIFS_STATS2
atomic_inc ( & ses - > server - > num_waiters ) ;
# endif
2005-04-17 02:20:36 +04:00
wait_event ( ses - > server - > request_q ,
atomic_read ( & ses - > server - > inFlight )
< cifs_max_pending ) ;
2005-10-07 20:51:05 +04:00
# ifdef CONFIG_CIFS_STATS2
atomic_dec ( & ses - > server - > num_waiters ) ;
# endif
2005-04-17 02:20:36 +04:00
spin_lock ( & GlobalMid_Lock ) ;
} else {
2007-07-07 02:44:50 +04:00
if ( ses - > server - > tcpStatus = = CifsExiting ) {
2005-04-17 02:20:36 +04:00
spin_unlock ( & GlobalMid_Lock ) ;
return - ENOENT ;
}
2007-07-07 02:44:50 +04:00
/* can not count locking commands against total
as they are allowed to block on server */
2007-07-13 04:33:32 +04:00
2005-04-17 02:20:36 +04:00
/* update # of requests on the wire to server */
2007-11-14 01:41:37 +03:00
if ( long_op ! = CIFS_BLOCKING_OP )
2005-04-17 02:20:36 +04:00
atomic_inc ( & ses - > server - > inFlight ) ;
spin_unlock ( & GlobalMid_Lock ) ;
break ;
}
}
}
2006-08-03 01:56:33 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2006-08-03 01:56:33 +04:00
static int allocate_mid ( struct cifsSesInfo * ses , struct smb_hdr * in_buf ,
struct mid_q_entry * * ppmidQ )
{
2005-04-17 02:20:36 +04:00
if ( ses - > server - > tcpStatus = = CifsExiting ) {
2006-08-03 01:56:33 +04:00
return - ENOENT ;
2005-04-17 02:20:36 +04:00
} else if ( ses - > server - > tcpStatus = = CifsNeedReconnect ) {
2007-07-07 02:44:50 +04:00
cFYI ( 1 , ( " tcp session dead - return to caller to retry " ) ) ;
2006-08-03 01:56:33 +04:00
return - EAGAIN ;
2005-04-17 02:20:36 +04:00
} else if ( ses - > status ! = CifsGood ) {
/* check if SMB session is bad because we are setting it up */
2007-07-07 02:44:50 +04:00
if ( ( in_buf - > Command ! = SMB_COM_SESSION_SETUP_ANDX ) & &
2008-02-08 02:25:02 +03:00
( in_buf - > Command ! = SMB_COM_NEGOTIATE ) )
2006-08-03 01:56:33 +04:00
return - EAGAIN ;
2008-02-08 02:25:02 +03:00
/* else ok - we are setting up session */
2005-04-17 02:20:36 +04:00
}
2008-12-01 15:09:34 +03:00
* ppmidQ = AllocMidQEntry ( in_buf , ses - > server ) ;
2007-08-31 02:09:15 +04:00
if ( * ppmidQ = = NULL )
2006-08-03 01:56:33 +04:00
return - ENOMEM ;
return 0 ;
}
2007-07-07 02:44:50 +04:00
static int wait_for_response ( struct cifsSesInfo * ses ,
2006-08-03 01:56:33 +04:00
struct mid_q_entry * midQ ,
unsigned long timeout ,
unsigned long time_to_wait )
{
unsigned long curr_timeout ;
for ( ; ; ) {
curr_timeout = timeout + jiffies ;
wait_event ( ses - > server - > response_q ,
2007-07-07 02:44:50 +04:00
( ! ( midQ - > midState = = MID_REQUEST_SUBMITTED ) ) | |
time_after ( jiffies , curr_timeout ) | |
2006-08-03 01:56:33 +04:00
( ( ses - > server - > tcpStatus ! = CifsGood ) & &
( ses - > server - > tcpStatus ! = CifsNew ) ) ) ;
if ( time_after ( jiffies , curr_timeout ) & &
( midQ - > midState = = MID_REQUEST_SUBMITTED ) & &
( ( ses - > server - > tcpStatus = = CifsGood ) | |
( ses - > server - > tcpStatus = = CifsNew ) ) ) {
unsigned long lrt ;
/* We timed out. Is the server still
sending replies ? */
spin_lock ( & GlobalMid_Lock ) ;
lrt = ses - > server - > lstrp ;
spin_unlock ( & GlobalMid_Lock ) ;
/* Calculate time_to_wait past last receive time.
2007-07-07 02:44:50 +04:00
Although we prefer not to time out if the
2006-08-03 01:56:33 +04:00
server is still responding - we will time
2007-07-07 02:44:50 +04:00
out if the server takes more than 15 ( or 45
2006-08-03 01:56:33 +04:00
or 180 ) seconds to respond to this request
2007-07-07 02:44:50 +04:00
and has not responded to any request from
2006-08-03 01:56:33 +04:00
other threads on the client within 10 seconds */
lrt + = time_to_wait ;
if ( time_after ( jiffies , lrt ) ) {
/* No replies for time_to_wait. */
2007-07-07 02:44:50 +04:00
cERROR ( 1 , ( " server not responding " ) ) ;
2006-08-03 01:56:33 +04:00
return - 1 ;
}
} else {
return 0 ;
}
}
}
2007-11-14 01:41:37 +03:00
/*
*
* Send an SMB Request . No response info ( other than return code )
* needs to be parsed .
*
* flags indicate the type of request buffer and how long to wait
* and whether to log NT STATUS code ( error ) before mapping it to POSIX error
*
*/
int
SendReceiveNoRsp ( const unsigned int xid , struct cifsSesInfo * ses ,
struct smb_hdr * in_buf , int flags )
{
int rc ;
struct kvec iov [ 1 ] ;
int resp_buf_type ;
iov [ 0 ] . iov_base = ( char * ) in_buf ;
iov [ 0 ] . iov_len = in_buf - > smb_buf_length + 4 ;
flags | = CIFS_NO_RESP ;
rc = SendReceive2 ( xid , ses , iov , 1 , & resp_buf_type , flags ) ;
2008-02-12 23:32:36 +03:00
cFYI ( DBG2 , ( " SendRcvNoRsp flags %d rc %d " , flags , rc ) ) ;
2007-11-14 01:41:37 +03:00
return rc ;
}
2006-08-03 01:56:33 +04:00
int
2007-07-07 02:44:50 +04:00
SendReceive2 ( const unsigned int xid , struct cifsSesInfo * ses ,
struct kvec * iov , int n_vec , int * pRespBufType /* ret */ ,
2007-11-14 01:41:37 +03:00
const int flags )
2006-08-03 01:56:33 +04:00
{
int rc = 0 ;
2007-11-14 01:41:37 +03:00
int long_op ;
2006-08-03 01:56:33 +04:00
unsigned int receive_len ;
unsigned long timeout ;
struct mid_q_entry * midQ ;
struct smb_hdr * in_buf = iov [ 0 ] . iov_base ;
2007-07-13 04:33:32 +04:00
2007-11-14 01:41:37 +03:00
long_op = flags & CIFS_TIMEOUT_MASK ;
2006-08-03 01:56:33 +04:00
* pRespBufType = CIFS_NO_BUFFER ; /* no response buf yet */
if ( ( ses = = NULL ) | | ( ses - > server = = NULL ) ) {
cifs_small_buf_release ( in_buf ) ;
2007-07-07 02:44:50 +04:00
cERROR ( 1 , ( " Null session " ) ) ;
2006-08-03 01:56:33 +04:00
return - EIO ;
}
2007-07-07 02:44:50 +04:00
if ( ses - > server - > tcpStatus = = CifsExiting ) {
2006-08-03 01:56:33 +04:00
cifs_small_buf_release ( in_buf ) ;
return - ENOENT ;
}
2007-07-07 02:44:50 +04:00
/* Ensure that we do not send more than 50 overlapping requests
2006-08-03 01:56:33 +04:00
to the same server . We may make this configurable later or
use ses - > maxReq */
rc = wait_for_free_request ( ses , long_op ) ;
if ( rc ) {
cifs_small_buf_release ( in_buf ) ;
return rc ;
}
2007-07-07 02:44:50 +04:00
/* make sure that we sign in the same order that we send on this socket
2006-08-03 01:56:33 +04:00
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
2008-12-01 15:09:36 +03:00
mutex_lock ( & ses - > server - > srv_mutex ) ;
2006-08-03 01:56:33 +04:00
rc = allocate_mid ( ses , in_buf , & midQ ) ;
if ( rc ) {
2008-12-01 15:09:36 +03:00
mutex_unlock ( & ses - > server - > srv_mutex ) ;
2006-02-26 19:41:18 +03:00
cifs_small_buf_release ( in_buf ) ;
2006-08-03 01:56:33 +04:00
/* Update # of requests on wire to server */
2007-07-07 02:44:50 +04:00
atomic_dec ( & ses - > server - > inFlight ) ;
2006-08-03 01:56:33 +04:00
wake_up ( & ses - > server - > request_q ) ;
return rc ;
2005-04-17 02:20:36 +04:00
}
2007-07-07 02:44:50 +04:00
rc = cifs_sign_smb2 ( iov , n_vec , ses - > server , & midQ - > sequence_number ) ;
2005-04-17 02:20:36 +04:00
midQ - > midState = MID_REQUEST_SUBMITTED ;
2005-10-07 20:51:05 +04:00
# ifdef CONFIG_CIFS_STATS2
atomic_inc ( & ses - > server - > inSend ) ;
# endif
2008-10-29 03:47:57 +03:00
rc = smb_send2 ( ses - > server , iov , n_vec ,
( struct sockaddr * ) & ( ses - > server - > addr . sockAddr ) ,
ses - > server - > noblocksnd ) ;
2005-10-07 20:51:05 +04:00
# ifdef CONFIG_CIFS_STATS2
atomic_dec ( & ses - > server - > inSend ) ;
2005-10-12 06:58:06 +04:00
midQ - > when_sent = jiffies ;
2005-10-07 20:51:05 +04:00
# endif
2006-08-03 01:56:33 +04:00
2008-12-01 15:09:36 +03:00
mutex_unlock ( & ses - > server - > srv_mutex ) ;
2006-08-03 01:56:33 +04:00
cifs_small_buf_release ( in_buf ) ;
2007-07-07 02:44:50 +04:00
if ( rc < 0 )
2006-08-03 01:56:33 +04:00
goto out ;
2006-02-26 19:41:18 +03:00
2007-11-14 01:41:37 +03:00
if ( long_op = = CIFS_STD_OP )
timeout = 15 * HZ ;
else if ( long_op = = CIFS_VLONG_OP ) /* e.g. slow writes past EOF */
2005-10-06 01:50:29 +04:00
timeout = 180 * HZ ;
2007-11-14 01:41:37 +03:00
else if ( long_op = = CIFS_LONG_OP )
2007-07-07 02:44:50 +04:00
timeout = 45 * HZ ; /* should be greater than
2005-06-13 22:24:43 +04:00
servers oplock break timeout ( about 43 seconds ) */
2007-11-14 01:41:37 +03:00
else if ( long_op = = CIFS_ASYNC_OP )
goto out ;
else if ( long_op = = CIFS_BLOCKING_OP )
timeout = 0x7FFFFFFF ; /* large, but not so large as to wrap */
else {
cERROR ( 1 , ( " unknown timeout flag %d " , long_op ) ) ;
rc = - EIO ;
goto out ;
}
2006-08-03 01:56:33 +04:00
2007-07-07 02:44:50 +04:00
/* wait for 15 seconds or until woken up due to response arriving or
2005-06-13 22:24:43 +04:00
due to last connection to this server being unmounted */
if ( signal_pending ( current ) ) {
/* if signal pending do not hold up user for full smb timeout
2007-03-06 03:31:00 +03:00
but we still give response a chance to complete */
2005-06-13 22:24:43 +04:00
timeout = 2 * HZ ;
2007-07-07 02:44:50 +04:00
}
2005-06-13 22:24:43 +04:00
/* No user interrupts in wait - wreaks havoc with performance */
2006-08-03 01:56:33 +04:00
wait_for_response ( ses , midQ , timeout , 10 * HZ ) ;
2005-06-13 22:24:43 +04:00
spin_lock ( & GlobalMid_Lock ) ;
if ( midQ - > resp_buf ) {
spin_unlock ( & GlobalMid_Lock ) ;
2005-09-23 03:32:06 +04:00
receive_len = midQ - > resp_buf - > smb_buf_length ;
2005-06-13 22:24:43 +04:00
} else {
2007-07-07 02:44:50 +04:00
cERROR ( 1 , ( " No response to cmd %d mid %d " ,
2005-10-06 01:50:29 +04:00
midQ - > command , midQ - > mid ) ) ;
2007-07-07 02:44:50 +04:00
if ( midQ - > midState = = MID_REQUEST_SUBMITTED ) {
if ( ses - > server - > tcpStatus = = CifsExiting )
2005-06-13 22:24:43 +04:00
rc = - EHOSTDOWN ;
else {
ses - > server - > tcpStatus = CifsNeedReconnect ;
midQ - > midState = MID_RETRY_NEEDED ;
}
}
if ( rc ! = - EHOSTDOWN ) {
2007-07-07 02:44:50 +04:00
if ( midQ - > midState = = MID_RETRY_NEEDED ) {
2005-06-13 22:24:43 +04:00
rc = - EAGAIN ;
2007-07-07 02:44:50 +04:00
cFYI ( 1 , ( " marking request for retry " ) ) ;
2005-06-13 22:24:43 +04:00
} else {
rc = - EIO ;
}
}
spin_unlock ( & GlobalMid_Lock ) ;
DeleteMidQEntry ( midQ ) ;
2006-08-03 01:56:33 +04:00
/* Update # of requests on wire to server */
2007-07-07 02:44:50 +04:00
atomic_dec ( & ses - > server - > inFlight ) ;
2006-08-03 01:56:33 +04:00
wake_up ( & ses - > server - > request_q ) ;
2005-06-13 22:24:43 +04:00
return rc ;
}
2007-07-13 04:33:32 +04:00
2005-06-13 22:24:43 +04:00
if ( receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE ) {
cERROR ( 1 , ( " Frame too large received. Length: %d Xid: %d " ,
receive_len , xid ) ) ;
rc = - EIO ;
} else { /* rcvd frame is ok */
2007-07-07 02:44:50 +04:00
if ( midQ - > resp_buf & &
2005-06-13 22:24:43 +04:00
( midQ - > midState = = MID_RESPONSE_RECEIVED ) ) {
2005-12-03 00:32:45 +03:00
2005-12-13 07:53:18 +03:00
iov [ 0 ] . iov_base = ( char * ) midQ - > resp_buf ;
2007-07-07 02:44:50 +04:00
if ( midQ - > largeBuf )
2005-12-13 07:53:18 +03:00
* pRespBufType = CIFS_LARGE_BUFFER ;
else
* pRespBufType = CIFS_SMALL_BUFFER ;
iov [ 0 ] . iov_len = receive_len + 4 ;
2005-06-13 22:24:43 +04:00
2005-12-13 07:53:18 +03:00
dump_smb ( midQ - > resp_buf , 80 ) ;
2005-06-13 22:24:43 +04:00
/* convert the length into a more usable form */
2007-07-07 02:44:50 +04:00
if ( ( receive_len > 24 ) & &
2005-06-13 22:24:43 +04:00
( ses - > server - > secMode & ( SECMODE_SIGN_REQUIRED |
SECMODE_SIGN_ENABLED ) ) ) {
2005-12-13 07:53:18 +03:00
rc = cifs_verify_signature ( midQ - > resp_buf ,
2007-07-09 11:55:14 +04:00
& ses - > server - > mac_signing_key ,
2005-06-13 22:24:43 +04:00
midQ - > sequence_number + 1 ) ;
2007-07-07 02:44:50 +04:00
if ( rc ) {
cERROR ( 1 , ( " Unexpected SMB signature " ) ) ;
2005-06-13 22:24:43 +04:00
/* BB FIXME add code to kill session */
}
}
/* BB special case reconnect tid and uid here? */
2007-11-14 01:41:37 +03:00
rc = map_smb_to_linux_error ( midQ - > resp_buf ,
flags & CIFS_LOG_ERROR ) ;
2005-06-13 22:24:43 +04:00
/* convert ByteCount if necessary */
2007-08-31 02:09:15 +04:00
if ( receive_len > = sizeof ( struct smb_hdr ) - 4
/* do not count RFC1001 header */ +
2005-12-13 07:53:18 +03:00
( 2 * midQ - > resp_buf - > WordCount ) + 2 /* bcc */ )
2007-07-07 02:44:50 +04:00
BCC ( midQ - > resp_buf ) =
2005-12-13 07:53:18 +03:00
le16_to_cpu ( BCC_LE ( midQ - > resp_buf ) ) ;
2007-11-14 01:41:37 +03:00
if ( ( flags & CIFS_NO_RESP ) = = 0 )
midQ - > resp_buf = NULL ; /* mark it so buf will
not be freed by
DeleteMidQEntry */
2005-06-13 22:24:43 +04:00
} else {
rc = - EIO ;
2007-07-07 02:44:50 +04:00
cFYI ( 1 , ( " Bad MID state? " ) ) ;
2005-06-13 22:24:43 +04:00
}
}
2005-04-17 02:20:36 +04:00
2006-08-03 01:56:33 +04:00
out :
DeleteMidQEntry ( midQ ) ;
2007-07-07 02:44:50 +04:00
atomic_dec ( & ses - > server - > inFlight ) ;
2006-08-03 01:56:33 +04:00
wake_up ( & ses - > server - > request_q ) ;
2005-04-17 02:20:36 +04:00
2005-06-13 22:24:43 +04:00
return rc ;
}
2005-04-17 02:20:36 +04:00
int
SendReceive ( const unsigned int xid , struct cifsSesInfo * ses ,
struct smb_hdr * in_buf , struct smb_hdr * out_buf ,
int * pbytes_returned , const int long_op )
{
int rc = 0 ;
unsigned int receive_len ;
unsigned long timeout ;
struct mid_q_entry * midQ ;
if ( ses = = NULL ) {
2007-07-07 02:44:50 +04:00
cERROR ( 1 , ( " Null smb session " ) ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2007-07-07 02:44:50 +04:00
if ( ses - > server = = NULL ) {
cERROR ( 1 , ( " Null tcp session " ) ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2007-07-07 02:44:50 +04:00
if ( ses - > server - > tcpStatus = = CifsExiting )
2005-04-29 09:41:11 +04:00
return - ENOENT ;
2007-07-07 02:44:50 +04:00
/* Ensure that we do not send more than 50 overlapping requests
2005-04-17 02:20:36 +04:00
to the same server . We may make this configurable later or
use ses - > maxReq */
2006-08-03 01:56:33 +04:00
rc = wait_for_free_request ( ses , long_op ) ;
if ( rc )
return rc ;
2007-07-07 02:44:50 +04:00
/* make sure that we sign in the same order that we send on this socket
2005-04-17 02:20:36 +04:00
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
2008-12-01 15:09:36 +03:00
mutex_lock ( & ses - > server - > srv_mutex ) ;
2005-04-17 02:20:36 +04:00
2006-08-03 01:56:33 +04:00
rc = allocate_mid ( ses , in_buf , & midQ ) ;
if ( rc ) {
2008-12-01 15:09:36 +03:00
mutex_unlock ( & ses - > server - > srv_mutex ) ;
2006-08-03 01:56:33 +04:00
/* Update # of requests on wire to server */
2007-07-07 02:44:50 +04:00
atomic_dec ( & ses - > server - > inFlight ) ;
2006-08-03 01:56:33 +04:00
wake_up ( & ses - > server - > request_q ) ;
return rc ;
2005-04-17 02:20:36 +04:00
}
if ( in_buf - > smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4 ) {
2006-05-31 22:05:34 +04:00
cERROR ( 1 , ( " Illegal length, greater than maximum frame, %d " ,
2005-04-17 02:20:36 +04:00
in_buf - > smb_buf_length ) ) ;
DeleteMidQEntry ( midQ ) ;
2008-12-01 15:09:36 +03:00
mutex_unlock ( & ses - > server - > srv_mutex ) ;
2006-08-03 01:56:33 +04:00
/* Update # of requests on wire to server */
2007-07-07 02:44:50 +04:00
atomic_dec ( & ses - > server - > inFlight ) ;
2006-08-03 01:56:33 +04:00
wake_up ( & ses - > server - > request_q ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2005-04-29 09:41:05 +04:00
rc = cifs_sign_smb ( in_buf , ses - > server , & midQ - > sequence_number ) ;
2005-04-17 02:20:36 +04:00
midQ - > midState = MID_REQUEST_SUBMITTED ;
2005-10-07 20:51:05 +04:00
# ifdef CONFIG_CIFS_STATS2
atomic_inc ( & ses - > server - > inSend ) ;
# endif
2005-04-17 02:20:36 +04:00
rc = smb_send ( ses - > server - > ssocket , in_buf , in_buf - > smb_buf_length ,
2008-10-29 03:47:57 +03:00
( struct sockaddr * ) & ( ses - > server - > addr . sockAddr ) ,
ses - > server - > noblocksnd ) ;
2005-10-07 20:51:05 +04:00
# ifdef CONFIG_CIFS_STATS2
atomic_dec ( & ses - > server - > inSend ) ;
2005-10-12 06:58:06 +04:00
midQ - > when_sent = jiffies ;
2005-10-07 20:51:05 +04:00
# endif
2008-12-01 15:09:36 +03:00
mutex_unlock ( & ses - > server - > srv_mutex ) ;
2006-08-03 01:56:33 +04:00
2007-07-07 02:44:50 +04:00
if ( rc < 0 )
2006-08-03 01:56:33 +04:00
goto out ;
2007-11-14 01:41:37 +03:00
if ( long_op = = CIFS_STD_OP )
timeout = 15 * HZ ;
/* wait for 15 seconds or until woken up due to response arriving or
due to last connection to this server being unmounted */
else if ( long_op = = CIFS_ASYNC_OP )
2006-08-03 01:56:33 +04:00
goto out ;
2007-11-14 01:41:37 +03:00
else if ( long_op = = CIFS_VLONG_OP ) /* writes past EOF can be slow */
2005-10-06 01:50:29 +04:00
timeout = 180 * HZ ;
2007-11-14 01:41:37 +03:00
else if ( long_op = = CIFS_LONG_OP )
2007-07-07 02:44:50 +04:00
timeout = 45 * HZ ; /* should be greater than
2005-04-17 02:20:36 +04:00
servers oplock break timeout ( about 43 seconds ) */
2007-11-14 01:41:37 +03:00
else if ( long_op = = CIFS_BLOCKING_OP )
timeout = 0x7FFFFFFF ; /* large but no so large as to wrap */
else {
cERROR ( 1 , ( " unknown timeout flag %d " , long_op ) ) ;
rc = - EIO ;
goto out ;
}
2005-04-17 02:20:36 +04:00
if ( signal_pending ( current ) ) {
/* if signal pending do not hold up user for full smb timeout
2007-03-06 03:31:00 +03:00
but we still give response a chance to complete */
2005-04-17 02:20:36 +04:00
timeout = 2 * HZ ;
2007-07-07 02:44:50 +04:00
}
2005-04-17 02:20:36 +04:00
/* No user interrupts in wait - wreaks havoc with performance */
2006-08-03 01:56:33 +04:00
wait_for_response ( ses , midQ , timeout , 10 * HZ ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & GlobalMid_Lock ) ;
if ( midQ - > resp_buf ) {
spin_unlock ( & GlobalMid_Lock ) ;
2005-09-23 03:32:06 +04:00
receive_len = midQ - > resp_buf - > smb_buf_length ;
2005-04-17 02:20:36 +04:00
} else {
2007-07-07 02:44:50 +04:00
cERROR ( 1 , ( " No response for cmd %d mid %d " ,
2005-10-06 01:50:29 +04:00
midQ - > command , midQ - > mid ) ) ;
2007-07-07 02:44:50 +04:00
if ( midQ - > midState = = MID_REQUEST_SUBMITTED ) {
if ( ses - > server - > tcpStatus = = CifsExiting )
2005-04-17 02:20:36 +04:00
rc = - EHOSTDOWN ;
else {
ses - > server - > tcpStatus = CifsNeedReconnect ;
midQ - > midState = MID_RETRY_NEEDED ;
}
}
if ( rc ! = - EHOSTDOWN ) {
2007-07-07 02:44:50 +04:00
if ( midQ - > midState = = MID_RETRY_NEEDED ) {
2005-04-17 02:20:36 +04:00
rc = - EAGAIN ;
2007-07-07 02:44:50 +04:00
cFYI ( 1 , ( " marking request for retry " ) ) ;
2005-04-17 02:20:36 +04:00
} else {
rc = - EIO ;
}
}
spin_unlock ( & GlobalMid_Lock ) ;
DeleteMidQEntry ( midQ ) ;
2006-08-03 01:56:33 +04:00
/* Update # of requests on wire to server */
2007-07-07 02:44:50 +04:00
atomic_dec ( & ses - > server - > inFlight ) ;
2006-08-03 01:56:33 +04:00
wake_up ( & ses - > server - > request_q ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
2007-07-13 04:33:32 +04:00
2005-04-17 02:20:36 +04:00
if ( receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE ) {
2005-04-29 09:41:05 +04:00
cERROR ( 1 , ( " Frame too large received. Length: %d Xid: %d " ,
2005-04-17 02:20:36 +04:00
receive_len , xid ) ) ;
rc = - EIO ;
} else { /* rcvd frame is ok */
if ( midQ - > resp_buf & & out_buf
& & ( midQ - > midState = = MID_RESPONSE_RECEIVED ) ) {
out_buf - > smb_buf_length = receive_len ;
memcpy ( ( char * ) out_buf + 4 ,
( char * ) midQ - > resp_buf + 4 ,
receive_len ) ;
dump_smb ( out_buf , 92 ) ;
/* convert the length into a more usable form */
2007-07-07 02:44:50 +04:00
if ( ( receive_len > 24 ) & &
2005-04-29 09:41:05 +04:00
( ses - > server - > secMode & ( SECMODE_SIGN_REQUIRED |
SECMODE_SIGN_ENABLED ) ) ) {
rc = cifs_verify_signature ( out_buf ,
2007-07-09 11:55:14 +04:00
& ses - > server - > mac_signing_key ,
2005-04-29 09:41:05 +04:00
midQ - > sequence_number + 1 ) ;
2007-07-07 02:44:50 +04:00
if ( rc ) {
cERROR ( 1 , ( " Unexpected SMB signature " ) ) ;
2005-04-29 09:41:10 +04:00
/* BB FIXME add code to kill session */
2005-04-29 09:41:05 +04:00
}
2005-04-17 02:20:36 +04:00
}
* pbytes_returned = out_buf - > smb_buf_length ;
2005-04-29 09:41:05 +04:00
/* BB special case reconnect tid and uid here? */
2007-10-19 01:45:27 +04:00
rc = map_smb_to_linux_error ( out_buf , 0 /* no log */ ) ;
2005-04-17 02:20:36 +04:00
/* convert ByteCount if necessary */
2007-08-31 02:09:15 +04:00
if ( receive_len > = sizeof ( struct smb_hdr ) - 4
/* do not count RFC1001 header */ +
2005-04-17 02:20:36 +04:00
( 2 * out_buf - > WordCount ) + 2 /* bcc */ )
2005-11-17 01:25:50 +03:00
BCC ( out_buf ) = le16_to_cpu ( BCC_LE ( out_buf ) ) ;
2005-04-17 02:20:36 +04:00
} else {
rc = - EIO ;
2007-07-07 02:44:50 +04:00
cERROR ( 1 , ( " Bad MID state? " ) ) ;
2005-04-17 02:20:36 +04:00
}
}
2006-08-03 01:56:33 +04:00
out :
2005-04-17 02:20:36 +04:00
DeleteMidQEntry ( midQ ) ;
2007-07-07 02:44:50 +04:00
atomic_dec ( & ses - > server - > inFlight ) ;
2006-08-03 01:56:33 +04:00
wake_up ( & ses - > server - > request_q ) ;
2005-04-17 02:20:36 +04:00
2006-08-03 01:56:33 +04:00
return rc ;
}
2005-04-17 02:20:36 +04:00
2006-08-03 01:56:33 +04:00
/* Send an NT_CANCEL SMB to cause the POSIX blocking lock to return. */
static int
send_nt_cancel ( struct cifsTconInfo * tcon , struct smb_hdr * in_buf ,
struct mid_q_entry * midQ )
{
int rc = 0 ;
struct cifsSesInfo * ses = tcon - > ses ;
__u16 mid = in_buf - > Mid ;
header_assemble ( in_buf , SMB_COM_NT_CANCEL , tcon , 0 ) ;
in_buf - > Mid = mid ;
2008-12-01 15:09:36 +03:00
mutex_lock ( & ses - > server - > srv_mutex ) ;
2006-08-03 01:56:33 +04:00
rc = cifs_sign_smb ( in_buf , ses - > server , & midQ - > sequence_number ) ;
if ( rc ) {
2008-12-01 15:09:36 +03:00
mutex_unlock ( & ses - > server - > srv_mutex ) ;
2006-08-03 01:56:33 +04:00
return rc ;
}
rc = smb_send ( ses - > server - > ssocket , in_buf , in_buf - > smb_buf_length ,
2008-10-29 03:47:57 +03:00
( struct sockaddr * ) & ( ses - > server - > addr . sockAddr ) ,
ses - > server - > noblocksnd ) ;
2008-12-01 15:09:36 +03:00
mutex_unlock ( & ses - > server - > srv_mutex ) ;
2005-04-17 02:20:36 +04:00
return rc ;
2006-08-03 01:56:33 +04:00
}
/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
blocking lock to return . */
static int
send_lock_cancel ( const unsigned int xid , struct cifsTconInfo * tcon ,
struct smb_hdr * in_buf ,
struct smb_hdr * out_buf )
{
int bytes_returned ;
struct cifsSesInfo * ses = tcon - > ses ;
LOCK_REQ * pSMB = ( LOCK_REQ * ) in_buf ;
/* We just modify the current in_buf to change
the type of lock from LOCKING_ANDX_SHARED_LOCK
or LOCKING_ANDX_EXCLUSIVE_LOCK to
LOCKING_ANDX_CANCEL_LOCK . */
pSMB - > LockType = LOCKING_ANDX_CANCEL_LOCK | LOCKING_ANDX_LARGE_FILES ;
pSMB - > Timeout = 0 ;
pSMB - > hdr . Mid = GetNextMid ( ses - > server ) ;
return SendReceive ( xid , ses , in_buf , out_buf ,
2007-11-14 01:41:37 +03:00
& bytes_returned , CIFS_STD_OP ) ;
2006-08-03 01:56:33 +04:00
}
int
SendReceiveBlockingLock ( const unsigned int xid , struct cifsTconInfo * tcon ,
struct smb_hdr * in_buf , struct smb_hdr * out_buf ,
int * pbytes_returned )
{
int rc = 0 ;
int rstart = 0 ;
unsigned int receive_len ;
struct mid_q_entry * midQ ;
struct cifsSesInfo * ses ;
if ( tcon = = NULL | | tcon - > ses = = NULL ) {
2007-07-07 02:44:50 +04:00
cERROR ( 1 , ( " Null smb session " ) ) ;
2006-08-03 01:56:33 +04:00
return - EIO ;
}
ses = tcon - > ses ;
2007-07-07 02:44:50 +04:00
if ( ses - > server = = NULL ) {
cERROR ( 1 , ( " Null tcp session " ) ) ;
2006-08-03 01:56:33 +04:00
return - EIO ;
}
2007-07-07 02:44:50 +04:00
if ( ses - > server - > tcpStatus = = CifsExiting )
2006-08-03 01:56:33 +04:00
return - ENOENT ;
2007-07-07 02:44:50 +04:00
/* Ensure that we do not send more than 50 overlapping requests
2006-08-03 01:56:33 +04:00
to the same server . We may make this configurable later or
use ses - > maxReq */
2007-11-14 01:41:37 +03:00
rc = wait_for_free_request ( ses , CIFS_BLOCKING_OP ) ;
2006-08-03 01:56:33 +04:00
if ( rc )
return rc ;
2007-07-07 02:44:50 +04:00
/* make sure that we sign in the same order that we send on this socket
2006-08-03 01:56:33 +04:00
and avoid races inside tcp sendmsg code that could cause corruption
of smb data */
2008-12-01 15:09:36 +03:00
mutex_lock ( & ses - > server - > srv_mutex ) ;
2006-08-03 01:56:33 +04:00
rc = allocate_mid ( ses , in_buf , & midQ ) ;
if ( rc ) {
2008-12-01 15:09:36 +03:00
mutex_unlock ( & ses - > server - > srv_mutex ) ;
2006-08-03 01:56:33 +04:00
return rc ;
}
if ( in_buf - > smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4 ) {
2008-12-01 15:09:36 +03:00
mutex_unlock ( & ses - > server - > srv_mutex ) ;
2006-08-03 01:56:33 +04:00
cERROR ( 1 , ( " Illegal length, greater than maximum frame, %d " ,
in_buf - > smb_buf_length ) ) ;
DeleteMidQEntry ( midQ ) ;
return - EIO ;
}
rc = cifs_sign_smb ( in_buf , ses - > server , & midQ - > sequence_number ) ;
2005-04-17 02:20:36 +04:00
2006-08-03 01:56:33 +04:00
midQ - > midState = MID_REQUEST_SUBMITTED ;
# ifdef CONFIG_CIFS_STATS2
atomic_inc ( & ses - > server - > inSend ) ;
# endif
rc = smb_send ( ses - > server - > ssocket , in_buf , in_buf - > smb_buf_length ,
2008-10-29 03:47:57 +03:00
( struct sockaddr * ) & ( ses - > server - > addr . sockAddr ) ,
ses - > server - > noblocksnd ) ;
2006-08-03 01:56:33 +04:00
# ifdef CONFIG_CIFS_STATS2
atomic_dec ( & ses - > server - > inSend ) ;
midQ - > when_sent = jiffies ;
# endif
2008-12-01 15:09:36 +03:00
mutex_unlock ( & ses - > server - > srv_mutex ) ;
2006-08-03 01:56:33 +04:00
2007-07-07 02:44:50 +04:00
if ( rc < 0 ) {
2006-08-03 01:56:33 +04:00
DeleteMidQEntry ( midQ ) ;
return rc ;
}
/* Wait for a reply - allow signals to interrupt. */
rc = wait_event_interruptible ( ses - > server - > response_q ,
2007-07-07 02:44:50 +04:00
( ! ( midQ - > midState = = MID_REQUEST_SUBMITTED ) ) | |
2006-08-03 01:56:33 +04:00
( ( ses - > server - > tcpStatus ! = CifsGood ) & &
( ses - > server - > tcpStatus ! = CifsNew ) ) ) ;
/* Were we interrupted by a signal ? */
if ( ( rc = = - ERESTARTSYS ) & &
( midQ - > midState = = MID_REQUEST_SUBMITTED ) & &
( ( ses - > server - > tcpStatus = = CifsGood ) | |
( ses - > server - > tcpStatus = = CifsNew ) ) ) {
if ( in_buf - > Command = = SMB_COM_TRANSACTION2 ) {
/* POSIX lock. We send a NT_CANCEL SMB to cause the
blocking lock to return . */
rc = send_nt_cancel ( tcon , in_buf , midQ ) ;
if ( rc ) {
DeleteMidQEntry ( midQ ) ;
return rc ;
}
} else {
/* Windows lock. We send a LOCKINGX_CANCEL_LOCK
to cause the blocking lock to return . */
rc = send_lock_cancel ( xid , tcon , in_buf , out_buf ) ;
/* If we get -ENOLCK back the lock may have
already been removed . Don ' t exit in this case . */
if ( rc & & rc ! = - ENOLCK ) {
DeleteMidQEntry ( midQ ) ;
return rc ;
}
}
/* Wait 5 seconds for the response. */
2007-07-07 02:44:50 +04:00
if ( wait_for_response ( ses , midQ , 5 * HZ , 5 * HZ ) = = 0 ) {
2006-08-03 01:56:33 +04:00
/* We got the response - restart system call. */
rstart = 1 ;
}
}
spin_lock ( & GlobalMid_Lock ) ;
if ( midQ - > resp_buf ) {
spin_unlock ( & GlobalMid_Lock ) ;
receive_len = midQ - > resp_buf - > smb_buf_length ;
} else {
2007-07-07 02:44:50 +04:00
cERROR ( 1 , ( " No response for cmd %d mid %d " ,
2006-08-03 01:56:33 +04:00
midQ - > command , midQ - > mid ) ) ;
2007-07-07 02:44:50 +04:00
if ( midQ - > midState = = MID_REQUEST_SUBMITTED ) {
if ( ses - > server - > tcpStatus = = CifsExiting )
2006-08-03 01:56:33 +04:00
rc = - EHOSTDOWN ;
else {
ses - > server - > tcpStatus = CifsNeedReconnect ;
midQ - > midState = MID_RETRY_NEEDED ;
}
}
if ( rc ! = - EHOSTDOWN ) {
2007-07-07 02:44:50 +04:00
if ( midQ - > midState = = MID_RETRY_NEEDED ) {
2006-08-03 01:56:33 +04:00
rc = - EAGAIN ;
2007-07-07 02:44:50 +04:00
cFYI ( 1 , ( " marking request for retry " ) ) ;
2006-08-03 01:56:33 +04:00
} else {
rc = - EIO ;
}
}
spin_unlock ( & GlobalMid_Lock ) ;
DeleteMidQEntry ( midQ ) ;
return rc ;
2005-04-17 02:20:36 +04:00
}
2007-07-13 04:33:32 +04:00
2006-08-03 01:56:33 +04:00
if ( receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE ) {
cERROR ( 1 , ( " Frame too large received. Length: %d Xid: %d " ,
receive_len , xid ) ) ;
rc = - EIO ;
} else { /* rcvd frame is ok */
if ( midQ - > resp_buf & & out_buf
& & ( midQ - > midState = = MID_RESPONSE_RECEIVED ) ) {
out_buf - > smb_buf_length = receive_len ;
memcpy ( ( char * ) out_buf + 4 ,
( char * ) midQ - > resp_buf + 4 ,
receive_len ) ;
dump_smb ( out_buf , 92 ) ;
/* convert the length into a more usable form */
2007-07-07 02:44:50 +04:00
if ( ( receive_len > 24 ) & &
2006-08-03 01:56:33 +04:00
( ses - > server - > secMode & ( SECMODE_SIGN_REQUIRED |
SECMODE_SIGN_ENABLED ) ) ) {
rc = cifs_verify_signature ( out_buf ,
2007-07-09 11:55:14 +04:00
& ses - > server - > mac_signing_key ,
2006-08-03 01:56:33 +04:00
midQ - > sequence_number + 1 ) ;
2007-07-07 02:44:50 +04:00
if ( rc ) {
cERROR ( 1 , ( " Unexpected SMB signature " ) ) ;
2006-08-03 01:56:33 +04:00
/* BB FIXME add code to kill session */
}
}
* pbytes_returned = out_buf - > smb_buf_length ;
/* BB special case reconnect tid and uid here? */
2007-10-19 01:45:27 +04:00
rc = map_smb_to_linux_error ( out_buf , 0 /* no log */ ) ;
2005-04-17 02:20:36 +04:00
2006-08-03 01:56:33 +04:00
/* convert ByteCount if necessary */
2007-08-31 02:09:15 +04:00
if ( receive_len > = sizeof ( struct smb_hdr ) - 4
/* do not count RFC1001 header */ +
2006-08-03 01:56:33 +04:00
( 2 * out_buf - > WordCount ) + 2 /* bcc */ )
BCC ( out_buf ) = le16_to_cpu ( BCC_LE ( out_buf ) ) ;
} else {
rc = - EIO ;
2007-07-07 02:44:50 +04:00
cERROR ( 1 , ( " Bad MID state? " ) ) ;
2006-08-03 01:56:33 +04:00
}
}
DeleteMidQEntry ( midQ ) ;
if ( rstart & & rc = = - EACCES )
return - ERESTARTSYS ;
2005-04-17 02:20:36 +04:00
return rc ;
}