2011-12-27 16:12:43 +04:00
/*
* fs / cifs / smb2pdu . c
*
2013-06-24 03:43:37 +04:00
* Copyright ( C ) International Business Machines Corp . , 2009 , 2013
2011-12-27 16:12:43 +04:00
* Etersoft , 2012
* Author ( s ) : Steve French ( sfrench @ us . ibm . com )
* Pavel Shilovsky ( pshilovsky @ samba . org ) 2012
*
* Contains the routines for constructing the SMB2 PDUs themselves
*
* 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
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
/* SMB2 PDU handling routines here - except for leftovers (eg session setup) */
/* Note that there are handle based routines which must be */
/* treated slightly differently for reconnection purposes since we never */
/* want to reuse a stale file handle and only the caller knows the file info */
# include <linux/fs.h>
# include <linux/kernel.h>
# include <linux/vfs.h>
2012-09-19 03:20:29 +04:00
# include <linux/task_io_accounting_ops.h>
2011-12-27 16:12:43 +04:00
# include <linux/uaccess.h>
2012-09-19 03:20:29 +04:00
# include <linux/pagemap.h>
2011-12-27 16:12:43 +04:00
# include <linux/xattr.h>
# include "smb2pdu.h"
# include "cifsglob.h"
# include "cifsacl.h"
# include "cifsproto.h"
# include "smb2proto.h"
# include "cifs_unicode.h"
# include "cifs_debug.h"
# include "ntlmssp.h"
# include "smb2status.h"
2012-09-19 03:20:29 +04:00
# include "smb2glob.h"
2012-09-19 03:20:33 +04:00
# include "cifspdu.h"
2011-12-27 16:12:43 +04:00
/*
* The following table defines the expected " StructureSize " of SMB2 requests
* in order by SMB2 command . This is similar to " wct " in SMB / CIFS requests .
*
* Note that commands are defined in smb2pdu . h in le16 but the array below is
* indexed by command in host byte order .
*/
static const int smb2_req_struct_sizes [ NUMBER_OF_SMB2_COMMANDS ] = {
/* SMB2_NEGOTIATE */ 36 ,
/* SMB2_SESSION_SETUP */ 25 ,
/* SMB2_LOGOFF */ 4 ,
/* SMB2_TREE_CONNECT */ 9 ,
/* SMB2_TREE_DISCONNECT */ 4 ,
/* SMB2_CREATE */ 57 ,
/* SMB2_CLOSE */ 24 ,
/* SMB2_FLUSH */ 24 ,
/* SMB2_READ */ 49 ,
/* SMB2_WRITE */ 49 ,
/* SMB2_LOCK */ 48 ,
/* SMB2_IOCTL */ 57 ,
/* SMB2_CANCEL */ 4 ,
/* SMB2_ECHO */ 4 ,
/* SMB2_QUERY_DIRECTORY */ 33 ,
/* SMB2_CHANGE_NOTIFY */ 32 ,
/* SMB2_QUERY_INFO */ 41 ,
/* SMB2_SET_INFO */ 33 ,
/* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */
} ;
static void
smb2_hdr_assemble ( struct smb2_hdr * hdr , __le16 smb2_cmd /* command */ ,
const struct cifs_tcon * tcon )
{
struct smb2_pdu * pdu = ( struct smb2_pdu * ) hdr ;
char * temp = ( char * ) hdr ;
/* lookup word count ie StructureSize from table */
__u16 parmsize = smb2_req_struct_sizes [ le16_to_cpu ( smb2_cmd ) ] ;
/*
* smaller than SMALL_BUFFER_SIZE but bigger than fixed area of
* largest operations ( Create )
*/
memset ( temp , 0 , 256 ) ;
/* Note this is only network field converted to big endian */
hdr - > smb2_buf_length = cpu_to_be32 ( parmsize + sizeof ( struct smb2_hdr )
- 4 /* RFC 1001 length field itself not counted */ ) ;
hdr - > ProtocolId [ 0 ] = 0xFE ;
hdr - > ProtocolId [ 1 ] = ' S ' ;
hdr - > ProtocolId [ 2 ] = ' M ' ;
hdr - > ProtocolId [ 3 ] = ' B ' ;
hdr - > StructureSize = cpu_to_le16 ( 64 ) ;
hdr - > Command = smb2_cmd ;
hdr - > CreditRequest = cpu_to_le16 ( 2 ) ; /* BB make this dynamic */
hdr - > ProcessId = cpu_to_le32 ( ( __u16 ) current - > tgid ) ;
if ( ! tcon )
goto out ;
2013-06-24 03:43:37 +04:00
/* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */
/* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */
2013-06-27 02:52:17 +04:00
if ( ( tcon - > ses ) & &
( tcon - > ses - > server - > capabilities & SMB2_GLOBAL_CAP_LARGE_MTU ) )
2013-06-24 03:43:37 +04:00
hdr - > CreditCharge = cpu_to_le16 ( 1 ) ;
/* else CreditCharge MBZ */
2011-12-27 16:12:43 +04:00
hdr - > TreeId = tcon - > tid ;
/* Uid is not converted */
if ( tcon - > ses )
hdr - > SessionId = tcon - > ses - > Suid ;
2013-06-27 04:14:55 +04:00
/*
* If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have
* to pass the path on the Open SMB prefixed by \ \ server \ share .
* Not sure when we would need to do the augmented path ( if ever ) and
* setting this flag breaks the SMB2 open operation since it is
* illegal to send an empty path name ( without \ \ server \ share prefix )
* when the DFS flag is set in the SMB open header . We could
* consider setting the flag on all operations other than open
* but it is safer to net set it for now .
*/
/* if (tcon->share_flags & SHI1005_FLAGS_DFS)
hdr - > Flags | = SMB2_FLAGS_DFS_OPERATIONS ; */
2013-05-26 15:01:00 +04:00
if ( tcon - > ses & & tcon - > ses - > server & & tcon - > ses - > server - > sign )
2012-09-19 03:20:30 +04:00
hdr - > Flags | = SMB2_FLAGS_SIGNED ;
2011-12-27 16:12:43 +04:00
out :
pdu - > StructureSize2 = cpu_to_le16 ( parmsize ) ;
return ;
}
static int
smb2_reconnect ( __le16 smb2_command , struct cifs_tcon * tcon )
{
int rc = 0 ;
2011-12-27 16:23:34 +04:00
struct nls_table * nls_codepage ;
struct cifs_ses * ses ;
struct TCP_Server_Info * server ;
/*
* SMB2s NegProt , SessSetup , Logoff do not have tcon yet so
* check for tcp and smb session status done differently
* for those three - in the calling routine .
*/
if ( tcon = = NULL )
return rc ;
if ( smb2_command = = SMB2_TREE_CONNECT )
return rc ;
if ( tcon - > tidStatus = = CifsExiting ) {
/*
* only tree disconnect , open , and write ,
* ( and ulogoff which does not have tcon )
* are allowed as we start force umount .
*/
if ( ( smb2_command ! = SMB2_WRITE ) & &
( smb2_command ! = SMB2_CREATE ) & &
( smb2_command ! = SMB2_TREE_DISCONNECT ) ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " can not send cmd %d while umounting \n " ,
smb2_command ) ;
2011-12-27 16:23:34 +04:00
return - ENODEV ;
}
}
if ( ( ! tcon - > ses ) | | ( tcon - > ses - > status = = CifsExiting ) | |
( ! tcon - > ses - > server ) )
return - EIO ;
ses = tcon - > ses ;
server = ses - > server ;
/*
* Give demultiplex thread up to 10 seconds to reconnect , should be
* greater than cifs socket timeout which is 7 seconds
*/
while ( server - > tcpStatus = = CifsNeedReconnect ) {
/*
* Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
* here since they are implicitly done when session drops .
*/
switch ( smb2_command ) {
/*
* BB Should we keep oplock break and add flush to exceptions ?
*/
case SMB2_TREE_DISCONNECT :
case SMB2_CANCEL :
case SMB2_CLOSE :
case SMB2_OPLOCK_BREAK :
return - EAGAIN ;
}
wait_event_interruptible_timeout ( server - > response_q ,
( server - > tcpStatus ! = CifsNeedReconnect ) , 10 * HZ ) ;
/* are we still trying to reconnect? */
if ( server - > tcpStatus ! = CifsNeedReconnect )
break ;
/*
* on " soft " mounts we wait once . Hard mounts keep
* retrying until process is killed or server comes
* back on - line
*/
if ( ! tcon - > retry ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " gave up waiting on reconnect in smb_init \n " ) ;
2011-12-27 16:23:34 +04:00
return - EHOSTDOWN ;
}
}
if ( ! tcon - > ses - > need_reconnect & & ! tcon - > need_reconnect )
return rc ;
nls_codepage = load_nls_default ( ) ;
/*
* need to prevent multiple threads trying to simultaneously reconnect
* the same SMB session
*/
mutex_lock ( & tcon - > ses - > session_mutex ) ;
rc = cifs_negotiate_protocol ( 0 , tcon - > ses ) ;
if ( ! rc & & tcon - > ses - > need_reconnect )
rc = cifs_setup_session ( 0 , tcon - > ses , nls_codepage ) ;
if ( rc | | ! tcon - > need_reconnect ) {
mutex_unlock ( & tcon - > ses - > session_mutex ) ;
goto out ;
}
cifs_mark_open_files_invalid ( tcon ) ;
rc = SMB2_tcon ( 0 , tcon - > ses , tcon - > treeName , tcon , nls_codepage ) ;
mutex_unlock ( & tcon - > ses - > session_mutex ) ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " reconnect tcon rc = %d \n " , rc ) ;
2011-12-27 16:23:34 +04:00
if ( rc )
goto out ;
atomic_inc ( & tconInfoReconnectCount ) ;
out :
/*
* Check if handle based operation so we know whether we can continue
* or not without returning to caller to reset file handle .
*/
/*
* BB Is flush done by server on drop of tcp session ? Should we special
* case it and skip above ?
*/
switch ( smb2_command ) {
case SMB2_FLUSH :
case SMB2_READ :
case SMB2_WRITE :
case SMB2_LOCK :
case SMB2_IOCTL :
case SMB2_QUERY_DIRECTORY :
case SMB2_CHANGE_NOTIFY :
case SMB2_QUERY_INFO :
case SMB2_SET_INFO :
return - EAGAIN ;
}
unload_nls ( nls_codepage ) ;
2011-12-27 16:12:43 +04:00
return rc ;
}
/*
* Allocate and return pointer to an SMB request hdr , and set basic
* SMB information in the SMB header . If the return code is zero , this
* function must have filled in request_buf pointer .
*/
static int
small_smb2_init ( __le16 smb2_command , struct cifs_tcon * tcon ,
void * * request_buf )
{
int rc = 0 ;
rc = smb2_reconnect ( smb2_command , tcon ) ;
if ( rc )
return rc ;
/* BB eventually switch this to SMB2 specific small buf size */
* request_buf = cifs_small_buf_get ( ) ;
if ( * request_buf = = NULL ) {
/* BB should we add a retry in here if not a writepage? */
return - ENOMEM ;
}
smb2_hdr_assemble ( ( struct smb2_hdr * ) * request_buf , smb2_command , tcon ) ;
if ( tcon ! = NULL ) {
# ifdef CONFIG_CIFS_STATS2
uint16_t com_code = le16_to_cpu ( smb2_command ) ;
cifs_stats_inc ( & tcon - > stats . smb2_stats . smb2_com_sent [ com_code ] ) ;
# endif
cifs_stats_inc ( & tcon - > num_smbs_sent ) ;
}
return rc ;
}
/*
*
* SMB2 Worker functions follow :
*
* The general structure of the worker functions is :
* 1 ) Call smb2_init ( assembles SMB2 header )
* 2 ) Initialize SMB2 command specific fields in fixed length area of SMB
* 3 ) Call smb_sendrcv2 ( sends request on socket and waits for response )
* 4 ) Decode SMB2 command specific fields in the fixed length area
* 5 ) Decode variable length data area ( if any for this SMB2 command type )
* 6 ) Call free smb buffer
* 7 ) return
*
*/
int
SMB2_negotiate ( const unsigned int xid , struct cifs_ses * ses )
{
struct smb2_negotiate_req * req ;
struct smb2_negotiate_rsp * rsp ;
struct kvec iov [ 1 ] ;
int rc = 0 ;
int resp_buftype ;
2013-05-24 15:41:01 +04:00
struct TCP_Server_Info * server = ses - > server ;
2011-12-27 16:12:43 +04:00
int blob_offset , blob_length ;
char * security_blob ;
int flags = CIFS_NEG_OP ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Negotiate protocol \n " ) ;
2011-12-27 16:12:43 +04:00
2013-05-24 15:41:01 +04:00
if ( ! server ) {
WARN ( 1 , " %s: server is NULL! \n " , __func__ ) ;
return - EIO ;
2011-12-27 16:12:43 +04:00
}
rc = small_smb2_init ( SMB2_NEGOTIATE , NULL , ( void * * ) & req ) ;
if ( rc )
return rc ;
req - > hdr . SessionId = 0 ;
2012-10-01 21:26:22 +04:00
req - > Dialects [ 0 ] = cpu_to_le16 ( ses - > server - > vals - > protocol_id ) ;
2011-12-27 16:12:43 +04:00
2012-10-01 21:26:22 +04:00
req - > DialectCount = cpu_to_le16 ( 1 ) ; /* One vers= at a time for now */
inc_rfc1001_len ( req , 2 ) ;
2011-12-27 16:12:43 +04:00
/* only one of SMB2 signing flags may be set in SMB2 request */
2013-05-26 15:01:00 +04:00
if ( ses - > sign )
2013-06-13 04:59:03 +04:00
req - > SecurityMode = cpu_to_le16 ( SMB2_NEGOTIATE_SIGNING_REQUIRED ) ;
2013-05-26 15:01:00 +04:00
else if ( global_secflags & CIFSSEC_MAY_SIGN )
2013-06-13 04:59:03 +04:00
req - > SecurityMode = cpu_to_le16 ( SMB2_NEGOTIATE_SIGNING_ENABLED ) ;
2013-05-26 15:01:00 +04:00
else
req - > SecurityMode = 0 ;
2011-12-27 16:12:43 +04:00
2012-10-01 21:26:22 +04:00
req - > Capabilities = cpu_to_le32 ( ses - > server - > vals - > req_capabilities ) ;
2011-12-27 16:12:43 +04:00
2014-05-14 00:37:45 +04:00
/* ClientGUID must be zero for SMB2.02 dialect */
if ( ses - > server - > vals - > protocol_id = = SMB20_PROT_ID )
memset ( req - > ClientGUID , 0 , SMB2_CLIENT_GUID_SIZE ) ;
else
memcpy ( req - > ClientGUID , server - > client_guid ,
SMB2_CLIENT_GUID_SIZE ) ;
2012-09-19 17:22:44 +04:00
2011-12-27 16:12:43 +04:00
iov [ 0 ] . iov_base = ( char * ) req ;
/* 4 for rfc1002 length field */
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 ;
rc = SendReceive2 ( xid , ses , iov , 1 , & resp_buftype , flags ) ;
rsp = ( struct smb2_negotiate_rsp * ) iov [ 0 ] . iov_base ;
/*
* No tcon so can ' t do
* cifs_stats_inc ( & tcon - > stats . smb2_stats . smb2_com_fail [ SMB2 . . . ] ) ;
*/
if ( rc ! = 0 )
goto neg_exit ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " mode 0x%x \n " , rsp - > SecurityMode ) ;
2011-12-27 16:12:43 +04:00
2012-10-01 21:26:22 +04:00
/* BB we may eventually want to match the negotiated vs. requested
dialect , even though we are only requesting one at a time */
if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB20_PROT_ID ) )
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " negotiated smb2.0 dialect \n " ) ;
2012-10-01 21:26:22 +04:00
else if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB21_PROT_ID ) )
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " negotiated smb2.1 dialect \n " ) ;
2012-10-01 21:26:22 +04:00
else if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB30_PROT_ID ) )
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " negotiated smb3.0 dialect \n " ) ;
2013-06-13 07:48:41 +04:00
else if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB302_PROT_ID ) )
cifs_dbg ( FYI , " negotiated smb3.02 dialect \n " ) ;
2011-12-27 16:12:43 +04:00
else {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " Illegal dialect returned by server %d \n " ,
le16_to_cpu ( rsp - > DialectRevision ) ) ;
2011-12-27 16:12:43 +04:00
rc = - EIO ;
goto neg_exit ;
}
server - > dialect = le16_to_cpu ( rsp - > DialectRevision ) ;
2013-05-26 15:00:59 +04:00
/* SMB2 only has an extended negflavor */
server - > negflavor = CIFS_NEGFLAVOR_EXTENDED ;
2014-02-14 13:31:02 +04:00
/* set it to the maximum buffer size value we can send with 1 credit */
server - > maxBuf = min_t ( unsigned int , le32_to_cpu ( rsp - > MaxTransactSize ) ,
SMB2_MAX_BUFFER_SIZE ) ;
2011-12-27 16:12:43 +04:00
server - > max_read = le32_to_cpu ( rsp - > MaxReadSize ) ;
server - > max_write = le32_to_cpu ( rsp - > MaxWriteSize ) ;
/* BB Do we need to validate the SecurityMode? */
server - > sec_mode = le16_to_cpu ( rsp - > SecurityMode ) ;
server - > capabilities = le32_to_cpu ( rsp - > Capabilities ) ;
2012-07-13 13:58:14 +04:00
/* Internal types */
server - > capabilities | = SMB2_NT_FIND | SMB2_LARGE_FILES ;
2011-12-27 16:12:43 +04:00
security_blob = smb2_get_data_area_len ( & blob_offset , & blob_length ,
& rsp - > hdr ) ;
2013-06-26 00:33:41 +04:00
/*
* See MS - SMB2 section 2.2 .4 : if no blob , client picks default which
* for us will be
* ses - > sectype = RawNTLMSSP ;
* but for time being this is our only auth choice so doesn ' t matter .
* We just found a server which sets blob length to zero expecting raw .
*/
if ( blob_length = = 0 )
cifs_dbg ( FYI , " missing security blob on negprot \n " ) ;
2012-09-19 03:20:30 +04:00
2013-05-26 15:01:00 +04:00
rc = cifs_enable_signing ( server , ses - > sign ) ;
2011-12-27 16:12:43 +04:00
# ifdef CONFIG_SMB2_ASN1 /* BB REMOVEME when updated asn1.c ready */
2013-05-26 15:00:58 +04:00
if ( rc )
goto neg_exit ;
2013-06-26 00:33:41 +04:00
if ( blob_length )
rc = decode_neg_token_init ( security_blob , blob_length ,
2011-12-27 16:12:43 +04:00
& server - > sec_type ) ;
if ( rc = = 1 )
rc = 0 ;
else if ( rc = = 0 ) {
rc = - EIO ;
goto neg_exit ;
}
# endif
neg_exit :
free_rsp_buf ( resp_buftype , rsp ) ;
return rc ;
}
2011-12-27 16:22:00 +04:00
2013-11-20 09:44:46 +04:00
int smb3_validate_negotiate ( const unsigned int xid , struct cifs_tcon * tcon )
{
int rc = 0 ;
struct validate_negotiate_info_req vneg_inbuf ;
struct validate_negotiate_info_rsp * pneg_rsp ;
u32 rsplen ;
cifs_dbg ( FYI , " validate negotiate \n " ) ;
/*
* validation ioctl must be signed , so no point sending this if we
* can not sign it . We could eventually change this to selectively
* sign just this , the first and only signed request on a connection .
* This is good enough for now since a user who wants better security
* would also enable signing on the mount . Having validation of
* negotiate info for signed connections helps reduce attack vectors
*/
if ( tcon - > ses - > server - > sign = = false )
return 0 ; /* validation requires signing */
vneg_inbuf . Capabilities =
cpu_to_le32 ( tcon - > ses - > server - > vals - > req_capabilities ) ;
2014-05-13 03:48:12 +04:00
memcpy ( vneg_inbuf . Guid , tcon - > ses - > server - > client_guid ,
SMB2_CLIENT_GUID_SIZE ) ;
2013-11-20 09:44:46 +04:00
if ( tcon - > ses - > sign )
vneg_inbuf . SecurityMode =
cpu_to_le16 ( SMB2_NEGOTIATE_SIGNING_REQUIRED ) ;
else if ( global_secflags & CIFSSEC_MAY_SIGN )
vneg_inbuf . SecurityMode =
cpu_to_le16 ( SMB2_NEGOTIATE_SIGNING_ENABLED ) ;
else
vneg_inbuf . SecurityMode = 0 ;
vneg_inbuf . DialectCount = cpu_to_le16 ( 1 ) ;
vneg_inbuf . Dialects [ 0 ] =
cpu_to_le16 ( tcon - > ses - > server - > vals - > protocol_id ) ;
rc = SMB2_ioctl ( xid , tcon , NO_FILE_ID , NO_FILE_ID ,
FSCTL_VALIDATE_NEGOTIATE_INFO , true /* is_fsctl */ ,
( char * ) & vneg_inbuf , sizeof ( struct validate_negotiate_info_req ) ,
( char * * ) & pneg_rsp , & rsplen ) ;
if ( rc ! = 0 ) {
cifs_dbg ( VFS , " validate protocol negotiate failed: %d \n " , rc ) ;
return - EIO ;
}
if ( rsplen ! = sizeof ( struct validate_negotiate_info_rsp ) ) {
cifs_dbg ( VFS , " invalid size of protocol negotiate response \n " ) ;
return - EIO ;
}
/* check validate negotiate info response matches what we got earlier */
if ( pneg_rsp - > Dialect ! =
cpu_to_le16 ( tcon - > ses - > server - > vals - > protocol_id ) )
goto vneg_out ;
if ( pneg_rsp - > SecurityMode ! = cpu_to_le16 ( tcon - > ses - > server - > sec_mode ) )
goto vneg_out ;
/* do not validate server guid because not saved at negprot time yet */
if ( ( le32_to_cpu ( pneg_rsp - > Capabilities ) | SMB2_NT_FIND |
SMB2_LARGE_FILES ) ! = tcon - > ses - > server - > capabilities )
goto vneg_out ;
/* validate negotiate successful */
cifs_dbg ( FYI , " validate negotiate info successful \n " ) ;
return 0 ;
vneg_out :
cifs_dbg ( VFS , " protocol revalidation - security settings mismatch \n " ) ;
return - EIO ;
}
2011-12-27 16:22:00 +04:00
int
SMB2_sess_setup ( const unsigned int xid , struct cifs_ses * ses ,
const struct nls_table * nls_cp )
{
struct smb2_sess_setup_req * req ;
struct smb2_sess_setup_rsp * rsp = NULL ;
struct kvec iov [ 2 ] ;
int rc = 0 ;
int resp_buftype ;
__le32 phase = NtLmNegotiate ; /* NTLMSSP, if needed, is multistage */
2013-05-24 15:41:01 +04:00
struct TCP_Server_Info * server = ses - > server ;
2011-12-27 16:22:00 +04:00
u16 blob_length = 0 ;
char * security_blob ;
char * ntlmssp_blob = NULL ;
bool use_spnego = false ; /* else use raw ntlmssp */
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Session Setup \n " ) ;
2011-12-27 16:22:00 +04:00
2013-05-24 15:41:01 +04:00
if ( ! server ) {
WARN ( 1 , " %s: server is NULL! \n " , __func__ ) ;
return - EIO ;
2011-12-27 16:22:00 +04:00
}
2013-08-29 17:35:09 +04:00
/*
* If we are here due to reconnect , free per - smb session key
* in case signing was required .
*/
kfree ( ses - > auth_key . response ) ;
ses - > auth_key . response = NULL ;
2011-12-27 16:22:00 +04:00
/*
* If memory allocation is successful , caller of this function
* frees it .
*/
ses - > ntlmssp = kmalloc ( sizeof ( struct ntlmssp_auth ) , GFP_KERNEL ) ;
if ( ! ses - > ntlmssp )
return - ENOMEM ;
2013-08-29 17:35:10 +04:00
ses - > ntlmssp - > sesskey_per_smbsess = true ;
2011-12-27 16:22:00 +04:00
2013-06-13 04:52:14 +04:00
/* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */
ses - > sectype = RawNTLMSSP ;
2011-12-27 16:22:00 +04:00
ssetup_ntlmssp_authenticate :
if ( phase = = NtLmChallenge )
phase = NtLmAuthenticate ; /* if ntlmssp, now final phase */
rc = small_smb2_init ( SMB2_SESSION_SETUP , NULL , ( void * * ) & req ) ;
if ( rc )
return rc ;
req - > hdr . SessionId = 0 ; /* First session, not a reauthenticate */
req - > VcNumber = 0 ; /* MBZ */
/* to enable echos and oplocks */
req - > hdr . CreditRequest = cpu_to_le16 ( 3 ) ;
/* only one of SMB2 signing flags may be set in SMB2 request */
2013-05-26 15:01:00 +04:00
if ( server - > sign )
req - > SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED ;
else if ( global_secflags & CIFSSEC_MAY_SIGN ) /* one flag unlike MUST_ */
req - > SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED ;
else
req - > SecurityMode = 0 ;
2011-12-27 16:22:00 +04:00
req - > Capabilities = 0 ;
req - > Channel = 0 ; /* MBZ */
iov [ 0 ] . iov_base = ( char * ) req ;
/* 4 for rfc1002 length field and 1 for pad */
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 - 1 ;
if ( phase = = NtLmNegotiate ) {
ntlmssp_blob = kmalloc ( sizeof ( struct _NEGOTIATE_MESSAGE ) ,
GFP_KERNEL ) ;
if ( ntlmssp_blob = = NULL ) {
rc = - ENOMEM ;
goto ssetup_exit ;
}
build_ntlmssp_negotiate_blob ( ntlmssp_blob , ses ) ;
if ( use_spnego ) {
/* blob_length = build_spnego_ntlmssp_blob(
& security_blob ,
sizeof ( struct _NEGOTIATE_MESSAGE ) ,
ntlmssp_blob ) ; */
/* BB eventually need to add this */
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " spnego not supported for SMB2 yet \n " ) ;
2011-12-27 16:22:00 +04:00
rc = - EOPNOTSUPP ;
kfree ( ntlmssp_blob ) ;
goto ssetup_exit ;
} else {
blob_length = sizeof ( struct _NEGOTIATE_MESSAGE ) ;
/* with raw NTLMSSP we don't encapsulate in SPNEGO */
security_blob = ntlmssp_blob ;
}
} else if ( phase = = NtLmAuthenticate ) {
req - > hdr . SessionId = ses - > Suid ;
ntlmssp_blob = kzalloc ( sizeof ( struct _NEGOTIATE_MESSAGE ) + 500 ,
GFP_KERNEL ) ;
if ( ntlmssp_blob = = NULL ) {
rc = - ENOMEM ;
goto ssetup_exit ;
}
rc = build_ntlmssp_auth_blob ( ntlmssp_blob , & blob_length , ses ,
nls_cp ) ;
if ( rc ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " build_ntlmssp_auth_blob failed %d \n " ,
rc ) ;
2011-12-27 16:22:00 +04:00
goto ssetup_exit ; /* BB double check error handling */
}
if ( use_spnego ) {
/* blob_length = build_spnego_ntlmssp_blob(
& security_blob ,
blob_length ,
ntlmssp_blob ) ; */
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " spnego not supported for SMB2 yet \n " ) ;
2011-12-27 16:22:00 +04:00
rc = - EOPNOTSUPP ;
kfree ( ntlmssp_blob ) ;
goto ssetup_exit ;
} else {
security_blob = ntlmssp_blob ;
}
} else {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " illegal ntlmssp phase \n " ) ;
2011-12-27 16:22:00 +04:00
rc = - EIO ;
goto ssetup_exit ;
}
/* Testing shows that buffer offset must be at location of Buffer[0] */
req - > SecurityBufferOffset =
cpu_to_le16 ( sizeof ( struct smb2_sess_setup_req ) -
1 /* pad */ - 4 /* rfc1001 len */ ) ;
req - > SecurityBufferLength = cpu_to_le16 ( blob_length ) ;
iov [ 1 ] . iov_base = security_blob ;
iov [ 1 ] . iov_len = blob_length ;
inc_rfc1001_len ( req , blob_length - 1 /* pad */ ) ;
/* BB add code to build os and lm fields */
2012-12-09 08:36:29 +04:00
rc = SendReceive2 ( xid , ses , iov , 2 , & resp_buftype ,
CIFS_LOG_ERROR | CIFS_NEG_OP ) ;
2011-12-27 16:22:00 +04:00
kfree ( security_blob ) ;
rsp = ( struct smb2_sess_setup_rsp * ) iov [ 0 ] . iov_base ;
2012-09-25 11:00:09 +04:00
if ( resp_buftype ! = CIFS_NO_BUFFER & &
rsp - > hdr . Status = = STATUS_MORE_PROCESSING_REQUIRED ) {
2011-12-27 16:22:00 +04:00
if ( phase ! = NtLmNegotiate ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " Unexpected more processing error \n " ) ;
2011-12-27 16:22:00 +04:00
goto ssetup_exit ;
}
if ( offsetof ( struct smb2_sess_setup_rsp , Buffer ) - 4 ! =
2012-09-25 11:00:09 +04:00
le16_to_cpu ( rsp - > SecurityBufferOffset ) ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " Invalid security buffer offset %d \n " ,
le16_to_cpu ( rsp - > SecurityBufferOffset ) ) ;
2011-12-27 16:22:00 +04:00
rc = - EIO ;
goto ssetup_exit ;
}
/* NTLMSSP Negotiate sent now processing challenge (response) */
phase = NtLmChallenge ; /* process ntlmssp challenge */
rc = 0 ; /* MORE_PROCESSING is not an error here but expected */
ses - > Suid = rsp - > hdr . SessionId ;
rc = decode_ntlmssp_challenge ( rsp - > Buffer ,
le16_to_cpu ( rsp - > SecurityBufferLength ) , ses ) ;
}
/*
* BB eventually add code for SPNEGO decoding of NtlmChallenge blob ,
* but at least the raw NTLMSSP case works .
*/
/*
* No tcon so can ' t do
* cifs_stats_inc ( & tcon - > stats . smb2_stats . smb2_com_fail [ SMB2 . . . ] ) ;
*/
if ( rc ! = 0 )
goto ssetup_exit ;
ses - > session_flags = le16_to_cpu ( rsp - > SessionFlags ) ;
2013-11-16 09:50:24 +04:00
if ( ses - > session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA )
cifs_dbg ( VFS , " SMB3 encryption not supported yet \n " ) ;
2011-12-27 16:22:00 +04:00
ssetup_exit :
free_rsp_buf ( resp_buftype , rsp ) ;
/* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */
if ( ( phase = = NtLmChallenge ) & & ( rc = = 0 ) )
goto ssetup_ntlmssp_authenticate ;
2013-08-29 17:35:09 +04:00
if ( ! rc ) {
mutex_lock ( & server - > srv_mutex ) ;
2013-08-29 17:35:11 +04:00
if ( server - > sign & & server - > ops - > generate_signingkey ) {
rc = server - > ops - > generate_signingkey ( ses ) ;
kfree ( ses - > auth_key . response ) ;
ses - > auth_key . response = NULL ;
if ( rc ) {
cifs_dbg ( FYI ,
" SMB3 session key generation failed \n " ) ;
mutex_unlock ( & server - > srv_mutex ) ;
goto keygen_exit ;
}
}
2013-08-29 17:35:09 +04:00
if ( ! server - > session_estab ) {
server - > sequence_number = 0x2 ;
server - > session_estab = true ;
}
mutex_unlock ( & server - > srv_mutex ) ;
cifs_dbg ( FYI , " SMB2/3 session established successfully \n " ) ;
spin_lock ( & GlobalMid_Lock ) ;
ses - > status = CifsGood ;
ses - > need_reconnect = false ;
spin_unlock ( & GlobalMid_Lock ) ;
}
2013-08-29 17:35:11 +04:00
keygen_exit :
2013-08-29 17:35:09 +04:00
if ( ! server - > sign ) {
kfree ( ses - > auth_key . response ) ;
ses - > auth_key . response = NULL ;
}
kfree ( ses - > ntlmssp ) ;
2011-12-27 16:22:00 +04:00
return rc ;
}
int
SMB2_logoff ( const unsigned int xid , struct cifs_ses * ses )
{
struct smb2_logoff_req * req ; /* response is also trivial struct */
int rc = 0 ;
struct TCP_Server_Info * server ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " disconnect session %p \n " , ses ) ;
2011-12-27 16:22:00 +04:00
if ( ses & & ( ses - > server ) )
server = ses - > server ;
else
return - EIO ;
2013-10-03 14:44:45 +04:00
/* no need to send SMB logoff if uid already closed due to reconnect */
if ( ses - > need_reconnect )
goto smb2_session_already_dead ;
2011-12-27 16:22:00 +04:00
rc = small_smb2_init ( SMB2_LOGOFF , NULL , ( void * * ) & req ) ;
if ( rc )
return rc ;
/* since no tcon, smb2_init can not do this, so do here */
req - > hdr . SessionId = ses - > Suid ;
2013-05-26 15:01:00 +04:00
if ( server - > sign )
2012-09-19 03:20:30 +04:00
req - > hdr . Flags | = SMB2_FLAGS_SIGNED ;
2011-12-27 16:22:00 +04:00
rc = SendReceiveNoRsp ( xid , ses , ( char * ) & req - > hdr , 0 ) ;
/*
* No tcon so can ' t do
* cifs_stats_inc ( & tcon - > stats . smb2_stats . smb2_com_fail [ SMB2 . . . ] ) ;
*/
2013-10-03 14:44:45 +04:00
smb2_session_already_dead :
2011-12-27 16:22:00 +04:00
return rc ;
}
2011-12-27 16:04:00 +04:00
static inline void cifs_stats_fail_inc ( struct cifs_tcon * tcon , uint16_t code )
{
2012-05-28 15:19:39 +04:00
cifs_stats_inc ( & tcon - > stats . smb2_stats . smb2_com_failed [ code ] ) ;
2011-12-27 16:04:00 +04:00
}
# define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */ )
2013-11-15 21:26:24 +04:00
/* These are similar values to what Windows uses */
static inline void init_copy_chunk_defaults ( struct cifs_tcon * tcon )
{
tcon - > max_chunks = 256 ;
tcon - > max_bytes_chunk = 1048576 ;
tcon - > max_bytes_copy = 16777216 ;
}
2011-12-27 16:04:00 +04:00
int
SMB2_tcon ( const unsigned int xid , struct cifs_ses * ses , const char * tree ,
struct cifs_tcon * tcon , const struct nls_table * cp )
{
struct smb2_tree_connect_req * req ;
struct smb2_tree_connect_rsp * rsp = NULL ;
struct kvec iov [ 2 ] ;
int rc = 0 ;
int resp_buftype ;
int unc_path_len ;
struct TCP_Server_Info * server ;
__le16 * unc_path = NULL ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " TCON \n " ) ;
2011-12-27 16:04:00 +04:00
if ( ( ses - > server ) & & tree )
server = ses - > server ;
else
return - EIO ;
if ( tcon & & tcon - > bad_network_name )
return - ENOENT ;
unc_path = kmalloc ( MAX_SHARENAME_LENGTH * 2 , GFP_KERNEL ) ;
if ( unc_path = = NULL )
return - ENOMEM ;
unc_path_len = cifs_strtoUTF16 ( unc_path , tree , strlen ( tree ) , cp ) + 1 ;
unc_path_len * = 2 ;
if ( unc_path_len < 2 ) {
kfree ( unc_path ) ;
return - EINVAL ;
}
rc = small_smb2_init ( SMB2_TREE_CONNECT , tcon , ( void * * ) & req ) ;
if ( rc ) {
kfree ( unc_path ) ;
return rc ;
}
if ( tcon = = NULL ) {
/* since no tcon, smb2_init can not do this, so do here */
req - > hdr . SessionId = ses - > Suid ;
/* if (ses->server->sec_mode & SECMODE_SIGN_REQUIRED)
req - > hdr . Flags | = SMB2_FLAGS_SIGNED ; */
}
iov [ 0 ] . iov_base = ( char * ) req ;
/* 4 for rfc1002 length field and 1 for pad */
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 - 1 ;
/* Testing shows that buffer offset must be at location of Buffer[0] */
req - > PathOffset = cpu_to_le16 ( sizeof ( struct smb2_tree_connect_req )
- 1 /* pad */ - 4 /* do not count rfc1001 len field */ ) ;
req - > PathLength = cpu_to_le16 ( unc_path_len - 2 ) ;
iov [ 1 ] . iov_base = unc_path ;
iov [ 1 ] . iov_len = unc_path_len ;
inc_rfc1001_len ( req , unc_path_len - 1 /* pad */ ) ;
rc = SendReceive2 ( xid , ses , iov , 2 , & resp_buftype , 0 ) ;
rsp = ( struct smb2_tree_connect_rsp * ) iov [ 0 ] . iov_base ;
if ( rc ! = 0 ) {
if ( tcon ) {
cifs_stats_fail_inc ( tcon , SMB2_TREE_CONNECT_HE ) ;
tcon - > need_reconnect = true ;
}
goto tcon_error_exit ;
}
if ( tcon = = NULL ) {
ses - > ipc_tid = rsp - > hdr . TreeId ;
goto tcon_exit ;
}
if ( rsp - > ShareType & SMB2_SHARE_TYPE_DISK )
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " connection to disk share \n " ) ;
2011-12-27 16:04:00 +04:00
else if ( rsp - > ShareType & SMB2_SHARE_TYPE_PIPE ) {
tcon - > ipc = true ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " connection to pipe share \n " ) ;
2011-12-27 16:04:00 +04:00
} else if ( rsp - > ShareType & SMB2_SHARE_TYPE_PRINT ) {
tcon - > print = true ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " connection to printer \n " ) ;
2011-12-27 16:04:00 +04:00
} else {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " unknown share type %d \n " , rsp - > ShareType ) ;
2011-12-27 16:04:00 +04:00
rc = - EOPNOTSUPP ;
goto tcon_error_exit ;
}
tcon - > share_flags = le32_to_cpu ( rsp - > ShareFlags ) ;
2013-06-19 23:15:30 +04:00
tcon - > capabilities = rsp - > Capabilities ; /* we keep caps little endian */
2011-12-27 16:04:00 +04:00
tcon - > maximal_access = le32_to_cpu ( rsp - > MaximalAccess ) ;
tcon - > tidStatus = CifsGood ;
tcon - > need_reconnect = false ;
tcon - > tid = rsp - > hdr . TreeId ;
2013-06-24 10:57:47 +04:00
strlcpy ( tcon - > treeName , tree , sizeof ( tcon - > treeName ) ) ;
2011-12-27 16:04:00 +04:00
if ( ( rsp - > Capabilities & SMB2_SHARE_CAP_DFS ) & &
( ( tcon - > share_flags & SHI1005_FLAGS_DFS ) = = 0 ) )
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " DFS capability contradicts DFS flag \n " ) ;
2013-11-15 21:26:24 +04:00
init_copy_chunk_defaults ( tcon ) ;
2013-11-20 09:44:46 +04:00
if ( tcon - > ses - > server - > ops - > validate_negotiate )
rc = tcon - > ses - > server - > ops - > validate_negotiate ( xid , tcon ) ;
2011-12-27 16:04:00 +04:00
tcon_exit :
free_rsp_buf ( resp_buftype , rsp ) ;
kfree ( unc_path ) ;
return rc ;
tcon_error_exit :
if ( rsp - > hdr . Status = = STATUS_BAD_NETWORK_NAME ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " BAD_NETWORK_NAME: %s \n " , tree ) ;
2011-12-27 16:04:00 +04:00
tcon - > bad_network_name = true ;
}
goto tcon_exit ;
}
int
SMB2_tdis ( const unsigned int xid , struct cifs_tcon * tcon )
{
struct smb2_tree_disconnect_req * req ; /* response is trivial */
int rc = 0 ;
struct TCP_Server_Info * server ;
struct cifs_ses * ses = tcon - > ses ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Tree Disconnect \n " ) ;
2011-12-27 16:04:00 +04:00
if ( ses & & ( ses - > server ) )
server = ses - > server ;
else
return - EIO ;
if ( ( tcon - > need_reconnect ) | | ( tcon - > ses - > need_reconnect ) )
return 0 ;
rc = small_smb2_init ( SMB2_TREE_DISCONNECT , tcon , ( void * * ) & req ) ;
if ( rc )
return rc ;
rc = SendReceiveNoRsp ( xid , ses , ( char * ) & req - > hdr , 0 ) ;
if ( rc )
cifs_stats_fail_inc ( tcon , SMB2_TREE_DISCONNECT_HE ) ;
return rc ;
}
2011-12-26 22:58:46 +04:00
2012-09-19 17:22:44 +04:00
2013-07-04 18:41:09 +04:00
static struct create_durable *
create_durable_buf ( void )
{
struct create_durable * buf ;
buf = kzalloc ( sizeof ( struct create_durable ) , GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
2013-07-09 18:40:58 +04:00
( struct create_durable , Data ) ) ;
2013-07-04 18:41:09 +04:00
buf - > ccontext . DataLength = cpu_to_le32 ( 16 ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct create_durable , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
2014-05-14 16:29:40 +04:00
/* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */
2013-07-04 18:41:09 +04:00
buf - > Name [ 0 ] = ' D ' ;
buf - > Name [ 1 ] = ' H ' ;
buf - > Name [ 2 ] = ' n ' ;
buf - > Name [ 3 ] = ' Q ' ;
return buf ;
}
2013-07-09 18:40:58 +04:00
static struct create_durable *
create_reconnect_durable_buf ( struct cifs_fid * fid )
{
struct create_durable * buf ;
buf = kzalloc ( sizeof ( struct create_durable ) , GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
( struct create_durable , Data ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( 16 ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct create_durable , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
buf - > Data . Fid . PersistentFileId = fid - > persistent_fid ;
buf - > Data . Fid . VolatileFileId = fid - > volatile_fid ;
2014-05-14 16:29:40 +04:00
/* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */
2013-07-09 18:40:58 +04:00
buf - > Name [ 0 ] = ' D ' ;
buf - > Name [ 1 ] = ' H ' ;
buf - > Name [ 2 ] = ' n ' ;
buf - > Name [ 3 ] = ' C ' ;
return buf ;
}
2012-09-19 17:22:44 +04:00
static __u8
2013-09-05 21:30:16 +04:00
parse_lease_state ( struct TCP_Server_Info * server , struct smb2_create_rsp * rsp ,
unsigned int * epoch )
2012-09-19 17:22:44 +04:00
{
char * data_offset ;
2013-09-05 20:16:45 +04:00
struct create_context * cc ;
2013-07-09 19:44:56 +04:00
unsigned int next = 0 ;
char * name ;
2012-09-19 17:22:44 +04:00
2013-07-09 19:44:56 +04:00
data_offset = ( char * ) rsp + 4 + le32_to_cpu ( rsp - > CreateContextsOffset ) ;
2013-09-05 20:16:45 +04:00
cc = ( struct create_context * ) data_offset ;
2012-09-19 17:22:44 +04:00
do {
2013-09-05 20:16:45 +04:00
cc = ( struct create_context * ) ( ( char * ) cc + next ) ;
name = le16_to_cpu ( cc - > NameOffset ) + ( char * ) cc ;
if ( le16_to_cpu ( cc - > NameLength ) ! = 4 | |
2012-09-19 17:22:44 +04:00
strncmp ( name , " RqLs " , 4 ) ) {
2013-09-05 20:16:45 +04:00
next = le32_to_cpu ( cc - > Next ) ;
2012-09-19 17:22:44 +04:00
continue ;
}
2013-09-05 21:30:16 +04:00
return server - > ops - > parse_lease_buf ( cc , epoch ) ;
2013-07-09 19:44:56 +04:00
} while ( next ! = 0 ) ;
2012-09-19 17:22:44 +04:00
2013-09-05 20:16:45 +04:00
return 0 ;
2012-09-19 17:22:44 +04:00
}
2013-07-04 19:10:00 +04:00
static int
2013-09-04 13:07:41 +04:00
add_lease_context ( struct TCP_Server_Info * server , struct kvec * iov ,
unsigned int * num_iovec , __u8 * oplock )
2013-07-04 19:10:00 +04:00
{
struct smb2_create_req * req = iov [ 0 ] . iov_base ;
unsigned int num = * num_iovec ;
2013-09-04 13:07:41 +04:00
iov [ num ] . iov_base = server - > ops - > create_lease_buf ( oplock + 1 , * oplock ) ;
2013-07-04 19:10:00 +04:00
if ( iov [ num ] . iov_base = = NULL )
return - ENOMEM ;
2013-09-04 13:07:41 +04:00
iov [ num ] . iov_len = server - > vals - > create_lease_size ;
2013-07-04 19:10:00 +04:00
req - > RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE ;
if ( ! req - > CreateContextsOffset )
req - > CreateContextsOffset = cpu_to_le32 (
sizeof ( struct smb2_create_req ) - 4 +
iov [ num - 1 ] . iov_len ) ;
2013-09-04 13:07:41 +04:00
le32_add_cpu ( & req - > CreateContextsLength ,
server - > vals - > create_lease_size ) ;
inc_rfc1001_len ( & req - > hdr , server - > vals - > create_lease_size ) ;
2013-07-04 19:10:00 +04:00
* num_iovec = num + 1 ;
return 0 ;
}
2013-07-04 18:41:09 +04:00
static int
2013-07-09 18:40:58 +04:00
add_durable_context ( struct kvec * iov , unsigned int * num_iovec ,
struct cifs_open_parms * oparms )
2013-07-04 18:41:09 +04:00
{
struct smb2_create_req * req = iov [ 0 ] . iov_base ;
unsigned int num = * num_iovec ;
2013-07-09 18:40:58 +04:00
if ( oparms - > reconnect ) {
iov [ num ] . iov_base = create_reconnect_durable_buf ( oparms - > fid ) ;
/* indicate that we don't need to relock the file */
oparms - > reconnect = false ;
} else
iov [ num ] . iov_base = create_durable_buf ( ) ;
2013-07-04 18:41:09 +04:00
if ( iov [ num ] . iov_base = = NULL )
return - ENOMEM ;
iov [ num ] . iov_len = sizeof ( struct create_durable ) ;
if ( ! req - > CreateContextsOffset )
req - > CreateContextsOffset =
cpu_to_le32 ( sizeof ( struct smb2_create_req ) - 4 +
iov [ 1 ] . iov_len ) ;
2013-08-26 10:34:46 +04:00
le32_add_cpu ( & req - > CreateContextsLength , sizeof ( struct create_durable ) ) ;
2013-07-04 18:41:09 +04:00
inc_rfc1001_len ( & req - > hdr , sizeof ( struct create_durable ) ) ;
* num_iovec = num + 1 ;
return 0 ;
}
2011-12-26 22:58:46 +04:00
int
2013-07-09 18:20:30 +04:00
SMB2_open ( const unsigned int xid , struct cifs_open_parms * oparms , __le16 * path ,
2013-08-14 19:25:21 +04:00
__u8 * oplock , struct smb2_file_all_info * buf ,
struct smb2_err_rsp * * err_buf )
2011-12-26 22:58:46 +04:00
{
struct smb2_create_req * req ;
struct smb2_create_rsp * rsp ;
struct TCP_Server_Info * server ;
2013-07-09 18:20:30 +04:00
struct cifs_tcon * tcon = oparms - > tcon ;
2011-12-26 22:58:46 +04:00
struct cifs_ses * ses = tcon - > ses ;
2013-07-04 18:41:09 +04:00
struct kvec iov [ 4 ] ;
2011-12-26 22:58:46 +04:00
int resp_buftype ;
int uni_path_len ;
2012-09-19 17:22:44 +04:00
__le16 * copy_path = NULL ;
int copy_size ;
2011-12-26 22:58:46 +04:00
int rc = 0 ;
2013-07-04 19:41:24 +04:00
unsigned int num_iovecs = 2 ;
2013-07-05 12:21:26 +04:00
__u32 file_attributes = 0 ;
2014-05-24 16:42:02 +04:00
char * dhc_buf = NULL , * lc_buf = NULL ;
2011-12-26 22:58:46 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " create/open \n " ) ;
2011-12-26 22:58:46 +04:00
if ( ses & & ( ses - > server ) )
server = ses - > server ;
else
return - EIO ;
rc = small_smb2_init ( SMB2_CREATE , tcon , ( void * * ) & req ) ;
if ( rc )
return rc ;
2013-07-09 18:20:30 +04:00
if ( oparms - > create_options & CREATE_OPTION_READONLY )
2013-07-05 12:21:26 +04:00
file_attributes | = ATTR_READONLY ;
2011-12-26 22:58:46 +04:00
req - > ImpersonationLevel = IL_IMPERSONATION ;
2013-07-09 18:20:30 +04:00
req - > DesiredAccess = cpu_to_le32 ( oparms - > desired_access ) ;
2011-12-26 22:58:46 +04:00
/* File attributes ignored on open (used in create though) */
req - > FileAttributes = cpu_to_le32 ( file_attributes ) ;
req - > ShareAccess = FILE_SHARE_ALL_LE ;
2013-07-09 18:20:30 +04:00
req - > CreateDisposition = cpu_to_le32 ( oparms - > disposition ) ;
req - > CreateOptions = cpu_to_le32 ( oparms - > create_options & CREATE_OPTIONS_MASK ) ;
2011-12-26 22:58:46 +04:00
uni_path_len = ( 2 * UniStrnlen ( ( wchar_t * ) path , PATH_MAX ) ) + 2 ;
2013-07-04 19:41:24 +04:00
/* do not count rfc1001 len field */
req - > NameOffset = cpu_to_le16 ( sizeof ( struct smb2_create_req ) - 4 ) ;
2011-12-26 22:58:46 +04:00
iov [ 0 ] . iov_base = ( char * ) req ;
/* 4 for rfc1002 length field */
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 ;
/* MUST set path len (NameLength) to 0 opening root of share */
2013-07-04 19:41:24 +04:00
req - > NameLength = cpu_to_le16 ( uni_path_len - 2 ) ;
/* -1 since last byte is buf[0] which is sent below (path) */
iov [ 0 ] . iov_len - - ;
if ( uni_path_len % 8 ! = 0 ) {
copy_size = uni_path_len / 8 * 8 ;
if ( copy_size < uni_path_len )
copy_size + = 8 ;
copy_path = kzalloc ( copy_size , GFP_KERNEL ) ;
if ( ! copy_path )
return - ENOMEM ;
memcpy ( ( char * ) copy_path , ( const char * ) path ,
uni_path_len ) ;
uni_path_len = copy_size ;
path = copy_path ;
2011-12-26 22:58:46 +04:00
}
2013-07-04 19:41:24 +04:00
iov [ 1 ] . iov_len = uni_path_len ;
iov [ 1 ] . iov_base = path ;
/* -1 since last byte is buf[0] which was counted in smb2_buf_len */
inc_rfc1001_len ( req , uni_path_len - 1 ) ;
2012-09-19 17:22:44 +04:00
if ( ! server - > oplocks )
* oplock = SMB2_OPLOCK_LEVEL_NONE ;
2013-09-04 13:07:41 +04:00
if ( ! ( server - > capabilities & SMB2_GLOBAL_CAP_LEASING ) | |
2012-09-19 17:22:44 +04:00
* oplock = = SMB2_OPLOCK_LEVEL_NONE )
req - > RequestedOplockLevel = * oplock ;
else {
2013-09-04 13:07:41 +04:00
rc = add_lease_context ( server , iov , & num_iovecs , oplock ) ;
2013-07-04 19:10:00 +04:00
if ( rc ) {
2012-09-19 17:22:44 +04:00
cifs_small_buf_release ( req ) ;
kfree ( copy_path ) ;
2013-07-04 19:10:00 +04:00
return rc ;
2012-09-19 17:22:44 +04:00
}
2014-05-24 16:42:02 +04:00
lc_buf = iov [ num_iovecs - 1 ] . iov_base ;
2012-09-19 17:22:44 +04:00
}
2013-07-04 18:41:09 +04:00
if ( * oplock = = SMB2_OPLOCK_LEVEL_BATCH ) {
/* need to set Next field of lease context if we request it */
2013-09-04 13:07:41 +04:00
if ( server - > capabilities & SMB2_GLOBAL_CAP_LEASING ) {
2013-07-04 18:41:09 +04:00
struct create_context * ccontext =
( struct create_context * ) iov [ num_iovecs - 1 ] . iov_base ;
2013-07-10 21:50:57 +04:00
ccontext - > Next =
2013-09-04 13:07:41 +04:00
cpu_to_le32 ( server - > vals - > create_lease_size ) ;
2013-07-04 18:41:09 +04:00
}
2013-07-09 18:40:58 +04:00
rc = add_durable_context ( iov , & num_iovecs , oparms ) ;
2013-07-04 18:41:09 +04:00
if ( rc ) {
cifs_small_buf_release ( req ) ;
kfree ( copy_path ) ;
2014-05-24 16:42:02 +04:00
kfree ( lc_buf ) ;
2013-07-04 18:41:09 +04:00
return rc ;
}
2014-05-24 16:42:02 +04:00
dhc_buf = iov [ num_iovecs - 1 ] . iov_base ;
2013-07-04 18:41:09 +04:00
}
2011-12-26 22:58:46 +04:00
rc = SendReceive2 ( xid , ses , iov , num_iovecs , & resp_buftype , 0 ) ;
rsp = ( struct smb2_create_rsp * ) iov [ 0 ] . iov_base ;
if ( rc ! = 0 ) {
cifs_stats_fail_inc ( tcon , SMB2_CREATE_HE ) ;
2013-08-14 19:25:21 +04:00
if ( err_buf )
* err_buf = kmemdup ( rsp , get_rfc1002_length ( rsp ) + 4 ,
GFP_KERNEL ) ;
2011-12-26 22:58:46 +04:00
goto creat_exit ;
}
2013-07-09 18:20:30 +04:00
oparms - > fid - > persistent_fid = rsp - > PersistentFileId ;
oparms - > fid - > volatile_fid = rsp - > VolatileFileId ;
2012-09-19 03:20:26 +04:00
if ( buf ) {
memcpy ( buf , & rsp - > CreationTime , 32 ) ;
buf - > AllocationSize = rsp - > AllocationSize ;
buf - > EndOfFile = rsp - > EndofFile ;
buf - > Attributes = rsp - > FileAttributes ;
buf - > NumberOfLinks = cpu_to_le32 ( 1 ) ;
buf - > DeletePending = 0 ;
}
2012-09-19 03:20:33 +04:00
2012-09-19 17:22:44 +04:00
if ( rsp - > OplockLevel = = SMB2_OPLOCK_LEVEL_LEASE )
2013-09-05 21:30:16 +04:00
* oplock = parse_lease_state ( server , rsp , & oparms - > fid - > epoch ) ;
2012-09-19 17:22:44 +04:00
else
* oplock = rsp - > OplockLevel ;
2011-12-26 22:58:46 +04:00
creat_exit :
2012-09-19 17:22:44 +04:00
kfree ( copy_path ) ;
2014-05-24 16:42:02 +04:00
kfree ( lc_buf ) ;
kfree ( dhc_buf ) ;
2011-12-26 22:58:46 +04:00
free_rsp_buf ( resp_buftype , rsp ) ;
return rc ;
}
2013-06-25 09:20:49 +04:00
/*
* SMB2 IOCTL is used for both IOCTLs and FSCTLs
*/
int
SMB2_ioctl ( const unsigned int xid , struct cifs_tcon * tcon , u64 persistent_fid ,
u64 volatile_fid , u32 opcode , bool is_fsctl , char * in_data ,
u32 indatalen , char * * out_data , u32 * plen /* returned data len */ )
{
struct smb2_ioctl_req * req ;
struct smb2_ioctl_rsp * rsp ;
struct TCP_Server_Info * server ;
struct cifs_ses * ses = tcon - > ses ;
struct kvec iov [ 2 ] ;
int resp_buftype ;
int num_iovecs ;
int rc = 0 ;
cifs_dbg ( FYI , " SMB2 IOCTL \n " ) ;
2013-10-14 09:44:19 +04:00
* out_data = NULL ;
2013-06-25 09:20:49 +04:00
/* zero out returned data len, in case of error */
if ( plen )
* plen = 0 ;
if ( ses & & ( ses - > server ) )
server = ses - > server ;
else
return - EIO ;
rc = small_smb2_init ( SMB2_IOCTL , tcon , ( void * * ) & req ) ;
if ( rc )
return rc ;
req - > CtlCode = cpu_to_le32 ( opcode ) ;
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
if ( indatalen ) {
req - > InputCount = cpu_to_le32 ( indatalen ) ;
/* do not set InputOffset if no input data */
req - > InputOffset =
cpu_to_le32 ( offsetof ( struct smb2_ioctl_req , Buffer ) - 4 ) ;
iov [ 1 ] . iov_base = in_data ;
iov [ 1 ] . iov_len = indatalen ;
num_iovecs = 2 ;
} else
num_iovecs = 1 ;
req - > OutputOffset = 0 ;
req - > OutputCount = 0 ; /* MBZ */
/*
* Could increase MaxOutputResponse , but that would require more
* than one credit . Windows typically sets this smaller , but for some
* ioctls it may be useful to allow server to send more . No point
* limiting what the server can send as long as fits in one credit
*/
req - > MaxOutputResponse = cpu_to_le32 ( 0xFF00 ) ; /* < 64K uses 1 credit */
if ( is_fsctl )
req - > Flags = cpu_to_le32 ( SMB2_0_IOCTL_IS_FSCTL ) ;
else
req - > Flags = 0 ;
iov [ 0 ] . iov_base = ( char * ) req ;
2013-10-14 09:44:19 +04:00
/*
* If no input data , the size of ioctl struct in
* protocol spec still includes a 1 byte data buffer ,
* but if input data passed to ioctl , we do not
* want to double count this , so we do not send
* the dummy one byte of data in iovec [ 0 ] if sending
* input data ( in iovec [ 1 ] ) . We also must add 4 bytes
* in first iovec to allow for rfc1002 length field .
*/
if ( indatalen ) {
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 - 1 ;
inc_rfc1001_len ( req , indatalen - 1 ) ;
} else
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 ;
2013-06-25 09:20:49 +04:00
rc = SendReceive2 ( xid , ses , iov , num_iovecs , & resp_buftype , 0 ) ;
rsp = ( struct smb2_ioctl_rsp * ) iov [ 0 ] . iov_base ;
2013-11-17 04:05:28 +04:00
if ( ( rc ! = 0 ) & & ( rc ! = - EINVAL ) ) {
2013-06-25 09:20:49 +04:00
if ( tcon )
cifs_stats_fail_inc ( tcon , SMB2_IOCTL_HE ) ;
goto ioctl_exit ;
2013-11-17 04:05:28 +04:00
} else if ( rc = = - EINVAL ) {
if ( ( opcode ! = FSCTL_SRV_COPYCHUNK_WRITE ) & &
( opcode ! = FSCTL_SRV_COPYCHUNK ) ) {
if ( tcon )
cifs_stats_fail_inc ( tcon , SMB2_IOCTL_HE ) ;
goto ioctl_exit ;
}
2013-06-25 09:20:49 +04:00
}
/* check if caller wants to look at return data or just return rc */
if ( ( plen = = NULL ) | | ( out_data = = NULL ) )
goto ioctl_exit ;
* plen = le32_to_cpu ( rsp - > OutputCount ) ;
/* We check for obvious errors in the output buffer length and offset */
if ( * plen = = 0 )
goto ioctl_exit ; /* server returned no data */
else if ( * plen > 0xFF00 ) {
cifs_dbg ( VFS , " srv returned invalid ioctl length: %d \n " , * plen ) ;
* plen = 0 ;
rc = - EIO ;
goto ioctl_exit ;
}
if ( get_rfc1002_length ( rsp ) < le32_to_cpu ( rsp - > OutputOffset ) + * plen ) {
cifs_dbg ( VFS , " Malformed ioctl resp: len %d offset %d \n " , * plen ,
le32_to_cpu ( rsp - > OutputOffset ) ) ;
* plen = 0 ;
rc = - EIO ;
goto ioctl_exit ;
}
* out_data = kmalloc ( * plen , GFP_KERNEL ) ;
if ( * out_data = = NULL ) {
rc = - ENOMEM ;
goto ioctl_exit ;
}
memcpy ( * out_data , rsp - > hdr . ProtocolId + le32_to_cpu ( rsp - > OutputOffset ) ,
* plen ) ;
ioctl_exit :
free_rsp_buf ( resp_buftype , rsp ) ;
return rc ;
}
2013-10-15 00:31:32 +04:00
/*
* Individual callers to ioctl worker function follow
*/
int
SMB2_set_compression ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid )
{
int rc ;
struct compress_ioctl fsctl_input ;
char * ret_data = NULL ;
fsctl_input . CompressionState =
__constant_cpu_to_le16 ( COMPRESSION_FORMAT_DEFAULT ) ;
rc = SMB2_ioctl ( xid , tcon , persistent_fid , volatile_fid ,
FSCTL_SET_COMPRESSION , true /* is_fsctl */ ,
( char * ) & fsctl_input /* data input */ ,
2 /* in data len */ , & ret_data /* out data */ , NULL ) ;
cifs_dbg ( FYI , " set compression rc %d \n " , rc ) ;
return rc ;
}
2011-12-26 22:58:46 +04:00
int
SMB2_close ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid )
{
struct smb2_close_req * req ;
struct smb2_close_rsp * rsp ;
struct TCP_Server_Info * server ;
struct cifs_ses * ses = tcon - > ses ;
struct kvec iov [ 1 ] ;
int resp_buftype ;
int rc = 0 ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Close \n " ) ;
2011-12-26 22:58:46 +04:00
if ( ses & & ( ses - > server ) )
server = ses - > server ;
else
return - EIO ;
rc = small_smb2_init ( SMB2_CLOSE , tcon , ( void * * ) & req ) ;
if ( rc )
return rc ;
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
iov [ 0 ] . iov_base = ( char * ) req ;
/* 4 for rfc1002 length field */
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 ;
rc = SendReceive2 ( xid , ses , iov , 1 , & resp_buftype , 0 ) ;
rsp = ( struct smb2_close_rsp * ) iov [ 0 ] . iov_base ;
if ( rc ! = 0 ) {
if ( tcon )
cifs_stats_fail_inc ( tcon , SMB2_CLOSE_HE ) ;
goto close_exit ;
}
/* BB FIXME - decode close response, update inode for caching */
close_exit :
free_rsp_buf ( resp_buftype , rsp ) ;
return rc ;
}
2011-12-29 17:06:33 +04:00
static int
validate_buf ( unsigned int offset , unsigned int buffer_length ,
struct smb2_hdr * hdr , unsigned int min_buf_size )
{
unsigned int smb_len = be32_to_cpu ( hdr - > smb2_buf_length ) ;
char * end_of_smb = smb_len + 4 /* RFC1001 length field */ + ( char * ) hdr ;
char * begin_of_buf = 4 /* RFC1001 len field */ + offset + ( char * ) hdr ;
char * end_of_buf = begin_of_buf + buffer_length ;
if ( buffer_length < min_buf_size ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " buffer length %d smaller than minimum size %d \n " ,
buffer_length , min_buf_size ) ;
2011-12-29 17:06:33 +04:00
return - EINVAL ;
}
/* check if beyond RFC1001 maximum length */
if ( ( smb_len > 0x7FFFFF ) | | ( buffer_length > 0x7FFFFF ) ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " buffer length %d or smb length %d too large \n " ,
buffer_length , smb_len ) ;
2011-12-29 17:06:33 +04:00
return - EINVAL ;
}
if ( ( begin_of_buf > end_of_smb ) | | ( end_of_buf > end_of_smb ) ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " illegal server response, bad offset to data \n " ) ;
2011-12-29 17:06:33 +04:00
return - EINVAL ;
}
return 0 ;
}
/*
* If SMB buffer fields are valid , copy into temporary buffer to hold result .
* Caller must free buffer .
*/
static int
validate_and_copy_buf ( unsigned int offset , unsigned int buffer_length ,
struct smb2_hdr * hdr , unsigned int minbufsize ,
char * data )
{
char * begin_of_buf = 4 /* RFC1001 len field */ + offset + ( char * ) hdr ;
int rc ;
if ( ! data )
return - EINVAL ;
rc = validate_buf ( offset , buffer_length , hdr , minbufsize ) ;
if ( rc )
return rc ;
memcpy ( data , begin_of_buf , buffer_length ) ;
return 0 ;
}
2012-09-19 03:20:26 +04:00
static int
query_info ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , u8 info_class ,
size_t output_len , size_t min_len , void * data )
2011-12-29 17:06:33 +04:00
{
struct smb2_query_info_req * req ;
struct smb2_query_info_rsp * rsp = NULL ;
struct kvec iov [ 2 ] ;
int rc = 0 ;
int resp_buftype ;
struct TCP_Server_Info * server ;
struct cifs_ses * ses = tcon - > ses ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Query Info \n " ) ;
2011-12-29 17:06:33 +04:00
if ( ses & & ( ses - > server ) )
server = ses - > server ;
else
return - EIO ;
rc = small_smb2_init ( SMB2_QUERY_INFO , tcon , ( void * * ) & req ) ;
if ( rc )
return rc ;
req - > InfoType = SMB2_O_INFO_FILE ;
2012-09-19 03:20:26 +04:00
req - > FileInfoClass = info_class ;
2011-12-29 17:06:33 +04:00
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
/* 4 for rfc1002 length field and 1 for Buffer */
req - > InputBufferOffset =
cpu_to_le16 ( sizeof ( struct smb2_query_info_req ) - 1 - 4 ) ;
2012-09-19 03:20:26 +04:00
req - > OutputBufferLength = cpu_to_le32 ( output_len ) ;
2011-12-29 17:06:33 +04:00
iov [ 0 ] . iov_base = ( char * ) req ;
/* 4 for rfc1002 length field */
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 ;
rc = SendReceive2 ( xid , ses , iov , 1 , & resp_buftype , 0 ) ;
2012-09-19 16:03:26 +04:00
rsp = ( struct smb2_query_info_rsp * ) iov [ 0 ] . iov_base ;
2011-12-29 17:06:33 +04:00
if ( rc ) {
cifs_stats_fail_inc ( tcon , SMB2_QUERY_INFO_HE ) ;
goto qinf_exit ;
}
rc = validate_and_copy_buf ( le16_to_cpu ( rsp - > OutputBufferOffset ) ,
le32_to_cpu ( rsp - > OutputBufferLength ) ,
2012-09-19 03:20:26 +04:00
& rsp - > hdr , min_len , data ) ;
2011-12-29 17:06:33 +04:00
qinf_exit :
free_rsp_buf ( resp_buftype , rsp ) ;
return rc ;
}
2012-07-12 18:30:44 +04:00
2012-09-19 03:20:26 +04:00
int
SMB2_query_info ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid ,
struct smb2_file_all_info * data )
{
return query_info ( xid , tcon , persistent_fid , volatile_fid ,
FILE_ALL_INFORMATION ,
sizeof ( struct smb2_file_all_info ) + MAX_NAME * 2 ,
sizeof ( struct smb2_file_all_info ) , data ) ;
}
int
SMB2_get_srv_num ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , __le64 * uniqueid )
{
return query_info ( xid , tcon , persistent_fid , volatile_fid ,
FILE_INTERNAL_INFORMATION ,
sizeof ( struct smb2_file_internal_info ) ,
sizeof ( struct smb2_file_internal_info ) , uniqueid ) ;
}
2012-07-12 18:30:44 +04:00
/*
* This is a no - op for now . We ' re not really interested in the reply , but
* rather in the fact that the server sent one and that server - > lstrp
* gets updated .
*
* FIXME : maybe we should consider checking that the reply matches request ?
*/
static void
smb2_echo_callback ( struct mid_q_entry * mid )
{
struct TCP_Server_Info * server = mid - > callback_data ;
struct smb2_echo_rsp * smb2 = ( struct smb2_echo_rsp * ) mid - > resp_buf ;
unsigned int credits_received = 1 ;
if ( mid - > mid_state = = MID_RESPONSE_RECEIVED )
credits_received = le16_to_cpu ( smb2 - > hdr . CreditRequest ) ;
DeleteMidQEntry ( mid ) ;
add_credits ( server , credits_received , CIFS_ECHO_OP ) ;
}
int
SMB2_echo ( struct TCP_Server_Info * server )
{
struct smb2_echo_req * req ;
int rc = 0 ;
struct kvec iov ;
2012-09-19 03:20:35 +04:00
struct smb_rqst rqst = { . rq_iov = & iov ,
. rq_nvec = 1 } ;
2012-07-12 18:30:44 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " In echo request \n " ) ;
2012-07-12 18:30:44 +04:00
rc = small_smb2_init ( SMB2_ECHO , NULL , ( void * * ) & req ) ;
if ( rc )
return rc ;
req - > hdr . CreditRequest = cpu_to_le16 ( 1 ) ;
iov . iov_base = ( char * ) req ;
/* 4 for rfc1002 length field */
iov . iov_len = get_rfc1002_length ( req ) + 4 ;
2012-09-19 03:20:35 +04:00
rc = cifs_call_async ( server , & rqst , NULL , smb2_echo_callback , server ,
2012-07-12 18:30:44 +04:00
CIFS_ECHO_OP ) ;
if ( rc )
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Echo request failed: %d \n " , rc ) ;
2012-07-12 18:30:44 +04:00
cifs_small_buf_release ( req ) ;
return rc ;
}
2012-09-19 03:20:28 +04:00
int
SMB2_flush ( const unsigned int xid , struct cifs_tcon * tcon , u64 persistent_fid ,
u64 volatile_fid )
{
struct smb2_flush_req * req ;
struct TCP_Server_Info * server ;
struct cifs_ses * ses = tcon - > ses ;
struct kvec iov [ 1 ] ;
int resp_buftype ;
int rc = 0 ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Flush \n " ) ;
2012-09-19 03:20:28 +04:00
if ( ses & & ( ses - > server ) )
server = ses - > server ;
else
return - EIO ;
rc = small_smb2_init ( SMB2_FLUSH , tcon , ( void * * ) & req ) ;
if ( rc )
return rc ;
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
iov [ 0 ] . iov_base = ( char * ) req ;
/* 4 for rfc1002 length field */
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 ;
rc = SendReceive2 ( xid , ses , iov , 1 , & resp_buftype , 0 ) ;
if ( ( rc ! = 0 ) & & tcon )
cifs_stats_fail_inc ( tcon , SMB2_FLUSH_HE ) ;
free_rsp_buf ( resp_buftype , iov [ 0 ] . iov_base ) ;
return rc ;
}
2012-09-19 03:20:29 +04:00
/*
* To form a chain of read requests , any read requests after the first should
* have the end_of_chain boolean set to true .
*/
static int
smb2_new_read_req ( struct kvec * iov , struct cifs_io_parms * io_parms ,
unsigned int remaining_bytes , int request_type )
{
int rc = - EACCES ;
struct smb2_read_req * req = NULL ;
rc = small_smb2_init ( SMB2_READ , io_parms - > tcon , ( void * * ) & req ) ;
if ( rc )
return rc ;
if ( io_parms - > tcon - > ses - > server = = NULL )
return - ECONNABORTED ;
req - > hdr . ProcessId = cpu_to_le32 ( io_parms - > pid ) ;
req - > PersistentFileId = io_parms - > persistent_fid ;
req - > VolatileFileId = io_parms - > volatile_fid ;
req - > ReadChannelInfoOffset = 0 ; /* reserved */
req - > ReadChannelInfoLength = 0 ; /* reserved */
req - > Channel = 0 ; /* reserved */
req - > MinimumCount = 0 ;
req - > Length = cpu_to_le32 ( io_parms - > length ) ;
req - > Offset = cpu_to_le64 ( io_parms - > offset ) ;
if ( request_type & CHAINED_REQUEST ) {
if ( ! ( request_type & END_OF_CHAIN ) ) {
/* 4 for rfc1002 length field */
req - > hdr . NextCommand =
cpu_to_le32 ( get_rfc1002_length ( req ) + 4 ) ;
} else /* END_OF_CHAIN */
req - > hdr . NextCommand = 0 ;
if ( request_type & RELATED_REQUEST ) {
req - > hdr . Flags | = SMB2_FLAGS_RELATED_OPERATIONS ;
/*
* Related requests use info from previous read request
* in chain .
*/
req - > hdr . SessionId = 0xFFFFFFFF ;
req - > hdr . TreeId = 0xFFFFFFFF ;
req - > PersistentFileId = 0xFFFFFFFF ;
req - > VolatileFileId = 0xFFFFFFFF ;
}
}
if ( remaining_bytes > io_parms - > length )
req - > RemainingBytes = cpu_to_le32 ( remaining_bytes ) ;
else
req - > RemainingBytes = 0 ;
iov [ 0 ] . iov_base = ( char * ) req ;
/* 4 for rfc1002 length field */
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 ;
return rc ;
}
static void
smb2_readv_callback ( struct mid_q_entry * mid )
{
struct cifs_readdata * rdata = mid - > callback_data ;
struct cifs_tcon * tcon = tlink_tcon ( rdata - > cfile - > tlink ) ;
struct TCP_Server_Info * server = tcon - > ses - > server ;
2012-09-19 17:22:34 +04:00
struct smb2_hdr * buf = ( struct smb2_hdr * ) rdata - > iov . iov_base ;
2012-09-19 03:20:29 +04:00
unsigned int credits_received = 1 ;
2012-09-19 17:22:34 +04:00
struct smb_rqst rqst = { . rq_iov = & rdata - > iov ,
2012-09-19 17:22:32 +04:00
. rq_nvec = 1 ,
. rq_pages = rdata - > pages ,
. rq_npages = rdata - > nr_pages ,
. rq_pagesz = rdata - > pagesz ,
. rq_tailsz = rdata - > tailsz } ;
2012-09-19 03:20:29 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: mid=%llu state=%d result=%d bytes=%u \n " ,
__func__ , mid - > mid , mid - > mid_state , rdata - > result ,
rdata - > bytes ) ;
2012-09-19 03:20:29 +04:00
switch ( mid - > mid_state ) {
case MID_RESPONSE_RECEIVED :
credits_received = le16_to_cpu ( buf - > CreditRequest ) ;
/* result already set, check signature */
2013-05-26 15:01:00 +04:00
if ( server - > sign ) {
2012-09-19 03:20:30 +04:00
int rc ;
2012-09-19 03:20:34 +04:00
rc = smb2_verify_signature ( & rqst , server ) ;
2012-09-19 03:20:30 +04:00
if ( rc )
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " SMB signature verification returned error = %d \n " ,
rc ) ;
2012-09-19 03:20:30 +04:00
}
2012-09-19 03:20:29 +04:00
/* FIXME: should this be counted toward the initiating task? */
task_io_account_read ( rdata - > bytes ) ;
cifs_stats_bytes_read ( tcon , rdata - > bytes ) ;
break ;
case MID_REQUEST_SUBMITTED :
case MID_RETRY_NEEDED :
rdata - > result = - EAGAIN ;
break ;
default :
if ( rdata - > result ! = - ENODATA )
rdata - > result = - EIO ;
}
if ( rdata - > result )
cifs_stats_fail_inc ( tcon , SMB2_READ_HE ) ;
queue_work ( cifsiod_wq , & rdata - > work ) ;
DeleteMidQEntry ( mid ) ;
add_credits ( server , credits_received , 0 ) ;
}
/* smb2_async_readv - send an async write, and set up mid to handle result */
int
smb2_async_readv ( struct cifs_readdata * rdata )
{
int rc ;
struct smb2_hdr * buf ;
struct cifs_io_parms io_parms ;
2012-09-19 17:22:34 +04:00
struct smb_rqst rqst = { . rq_iov = & rdata - > iov ,
2012-09-19 03:20:35 +04:00
. rq_nvec = 1 } ;
2012-09-19 03:20:29 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: offset=%llu bytes=%u \n " ,
__func__ , rdata - > offset , rdata - > bytes ) ;
2012-09-19 03:20:29 +04:00
io_parms . tcon = tlink_tcon ( rdata - > cfile - > tlink ) ;
io_parms . offset = rdata - > offset ;
io_parms . length = rdata - > bytes ;
io_parms . persistent_fid = rdata - > cfile - > fid . persistent_fid ;
io_parms . volatile_fid = rdata - > cfile - > fid . volatile_fid ;
io_parms . pid = rdata - > pid ;
2012-09-19 17:22:34 +04:00
rc = smb2_new_read_req ( & rdata - > iov , & io_parms , 0 , 0 ) ;
2012-09-19 03:20:29 +04:00
if ( rc )
return rc ;
2012-09-19 17:22:34 +04:00
buf = ( struct smb2_hdr * ) rdata - > iov . iov_base ;
2012-09-19 03:20:29 +04:00
/* 4 for rfc1002 length field */
2012-09-19 17:22:34 +04:00
rdata - > iov . iov_len = get_rfc1002_length ( rdata - > iov . iov_base ) + 4 ;
2012-09-19 03:20:29 +04:00
kref_get ( & rdata - > refcount ) ;
2012-09-19 03:20:35 +04:00
rc = cifs_call_async ( io_parms . tcon - > ses - > server , & rqst ,
2012-09-19 03:20:29 +04:00
cifs_readv_receive , smb2_readv_callback ,
rdata , 0 ) ;
2012-09-19 16:03:26 +04:00
if ( rc ) {
2012-09-19 03:20:29 +04:00
kref_put ( & rdata - > refcount , cifs_readdata_release ) ;
2012-09-19 16:03:26 +04:00
cifs_stats_fail_inc ( io_parms . tcon , SMB2_READ_HE ) ;
}
2012-09-19 03:20:29 +04:00
cifs_small_buf_release ( buf ) ;
return rc ;
}
2012-09-19 03:20:29 +04:00
2012-09-19 03:20:30 +04:00
int
SMB2_read ( const unsigned int xid , struct cifs_io_parms * io_parms ,
unsigned int * nbytes , char * * buf , int * buf_type )
{
int resp_buftype , rc = - EACCES ;
struct smb2_read_rsp * rsp = NULL ;
struct kvec iov [ 1 ] ;
* nbytes = 0 ;
rc = smb2_new_read_req ( iov , io_parms , 0 , 0 ) ;
if ( rc )
return rc ;
rc = SendReceive2 ( xid , io_parms - > tcon - > ses , iov , 1 ,
& resp_buftype , CIFS_LOG_ERROR ) ;
rsp = ( struct smb2_read_rsp * ) iov [ 0 ] . iov_base ;
if ( rsp - > hdr . Status = = STATUS_END_OF_FILE ) {
free_rsp_buf ( resp_buftype , iov [ 0 ] . iov_base ) ;
return 0 ;
}
if ( rc ) {
cifs_stats_fail_inc ( io_parms - > tcon , SMB2_READ_HE ) ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " Send error in read = %d \n " , rc ) ;
2012-09-19 03:20:30 +04:00
} else {
* nbytes = le32_to_cpu ( rsp - > DataLength ) ;
if ( ( * nbytes > CIFS_MAX_MSGSIZE ) | |
( * nbytes > io_parms - > length ) ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " bad length %d for count %d \n " ,
* nbytes , io_parms - > length ) ;
2012-09-19 03:20:30 +04:00
rc = - EIO ;
* nbytes = 0 ;
}
}
if ( * buf ) {
memcpy ( * buf , ( char * ) rsp - > hdr . ProtocolId + rsp - > DataOffset ,
* nbytes ) ;
free_rsp_buf ( resp_buftype , iov [ 0 ] . iov_base ) ;
} else if ( resp_buftype ! = CIFS_NO_BUFFER ) {
* buf = iov [ 0 ] . iov_base ;
if ( resp_buftype = = CIFS_SMALL_BUFFER )
* buf_type = CIFS_SMALL_BUFFER ;
else if ( resp_buftype = = CIFS_LARGE_BUFFER )
* buf_type = CIFS_LARGE_BUFFER ;
}
return rc ;
}
2012-09-19 03:20:29 +04:00
/*
* Check the mid_state and signature on received buffer ( if any ) , and queue the
* workqueue completion task .
*/
static void
smb2_writev_callback ( struct mid_q_entry * mid )
{
struct cifs_writedata * wdata = mid - > callback_data ;
struct cifs_tcon * tcon = tlink_tcon ( wdata - > cfile - > tlink ) ;
unsigned int written ;
struct smb2_write_rsp * rsp = ( struct smb2_write_rsp * ) mid - > resp_buf ;
unsigned int credits_received = 1 ;
switch ( mid - > mid_state ) {
case MID_RESPONSE_RECEIVED :
credits_received = le16_to_cpu ( rsp - > hdr . CreditRequest ) ;
wdata - > result = smb2_check_receive ( mid , tcon - > ses - > server , 0 ) ;
if ( wdata - > result ! = 0 )
break ;
written = le32_to_cpu ( rsp - > DataLength ) ;
/*
* Mask off high 16 bits when bytes written as returned
* by the server is greater than bytes requested by the
* client . OS / 2 servers are known to set incorrect
* CountHigh values .
*/
if ( written > wdata - > bytes )
written & = 0xFFFF ;
if ( written < wdata - > bytes )
wdata - > result = - ENOSPC ;
else
wdata - > bytes = written ;
break ;
case MID_REQUEST_SUBMITTED :
case MID_RETRY_NEEDED :
wdata - > result = - EAGAIN ;
break ;
default :
wdata - > result = - EIO ;
break ;
}
if ( wdata - > result )
cifs_stats_fail_inc ( tcon , SMB2_WRITE_HE ) ;
queue_work ( cifsiod_wq , & wdata - > work ) ;
DeleteMidQEntry ( mid ) ;
add_credits ( tcon - > ses - > server , credits_received , 0 ) ;
}
/* smb2_async_writev - send an async write, and set up mid to handle result */
int
2014-02-08 06:45:12 +04:00
smb2_async_writev ( struct cifs_writedata * wdata ,
void ( * release ) ( struct kref * kref ) )
2012-09-19 03:20:29 +04:00
{
2014-06-05 19:03:27 +04:00
int rc = - EACCES , flags = 0 ;
2012-09-19 03:20:29 +04:00
struct smb2_write_req * req = NULL ;
struct cifs_tcon * tcon = tlink_tcon ( wdata - > cfile - > tlink ) ;
2014-06-05 19:03:27 +04:00
struct TCP_Server_Info * server = tcon - > ses - > server ;
2012-09-19 03:20:35 +04:00
struct kvec iov ;
2012-09-19 03:20:35 +04:00
struct smb_rqst rqst ;
2012-09-19 03:20:29 +04:00
rc = small_smb2_init ( SMB2_WRITE , tcon , ( void * * ) & req ) ;
2014-06-05 19:03:27 +04:00
if ( rc ) {
if ( rc = = - EAGAIN & & wdata - > credits ) {
/* credits was reset by reconnect */
wdata - > credits = 0 ;
/* reduce in_flight value since we won't send the req */
spin_lock ( & server - > req_lock ) ;
server - > in_flight - - ;
spin_unlock ( & server - > req_lock ) ;
}
2012-09-19 03:20:29 +04:00
goto async_writev_out ;
2014-06-05 19:03:27 +04:00
}
2012-09-19 03:20:29 +04:00
req - > hdr . ProcessId = cpu_to_le32 ( wdata - > cfile - > pid ) ;
req - > PersistentFileId = wdata - > cfile - > fid . persistent_fid ;
req - > VolatileFileId = wdata - > cfile - > fid . volatile_fid ;
req - > WriteChannelInfoOffset = 0 ;
req - > WriteChannelInfoLength = 0 ;
req - > Channel = 0 ;
req - > Offset = cpu_to_le64 ( wdata - > offset ) ;
/* 4 for rfc1002 length field */
req - > DataOffset = cpu_to_le16 (
offsetof ( struct smb2_write_req , Buffer ) - 4 ) ;
req - > RemainingBytes = 0 ;
/* 4 for rfc1002 length field and 1 for Buffer */
2012-09-19 03:20:35 +04:00
iov . iov_len = get_rfc1002_length ( req ) + 4 - 1 ;
iov . iov_base = req ;
2012-09-19 03:20:29 +04:00
2012-09-19 03:20:35 +04:00
rqst . rq_iov = & iov ;
rqst . rq_nvec = 1 ;
rqst . rq_pages = wdata - > pages ;
rqst . rq_npages = wdata - > nr_pages ;
rqst . rq_pagesz = wdata - > pagesz ;
rqst . rq_tailsz = wdata - > tailsz ;
2012-09-19 03:20:29 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " async write at %llu %u bytes \n " ,
wdata - > offset , wdata - > bytes ) ;
2012-09-19 03:20:29 +04:00
req - > Length = cpu_to_le32 ( wdata - > bytes ) ;
inc_rfc1001_len ( & req - > hdr , wdata - > bytes - 1 /* Buffer */ ) ;
2014-06-05 19:03:27 +04:00
if ( wdata - > credits ) {
req - > hdr . CreditCharge = cpu_to_le16 ( DIV_ROUND_UP ( wdata - > bytes ,
SMB2_MAX_BUFFER_SIZE ) ) ;
spin_lock ( & server - > req_lock ) ;
server - > credits + = wdata - > credits -
le16_to_cpu ( req - > hdr . CreditCharge ) ;
spin_unlock ( & server - > req_lock ) ;
wake_up ( & server - > request_q ) ;
flags = CIFS_HAS_CREDITS ;
}
2012-09-19 03:20:29 +04:00
kref_get ( & wdata - > refcount ) ;
2014-06-05 19:03:27 +04:00
rc = cifs_call_async ( server , & rqst , NULL , smb2_writev_callback , wdata ,
flags ) ;
2012-09-19 03:20:29 +04:00
2012-09-19 16:03:26 +04:00
if ( rc ) {
2014-02-08 06:45:12 +04:00
kref_put ( & wdata - > refcount , release ) ;
2012-09-19 16:03:26 +04:00
cifs_stats_fail_inc ( tcon , SMB2_WRITE_HE ) ;
}
2012-09-19 03:20:29 +04:00
async_writev_out :
cifs_small_buf_release ( req ) ;
return rc ;
}
2012-09-19 03:20:30 +04:00
/*
* SMB2_write function gets iov pointer to kvec array with n_vec as a length .
* The length field from io_parms must be at least 1 and indicates a number of
* elements with data to write that begins with position 1 in iov array . All
* data length is specified by count .
*/
int
SMB2_write ( const unsigned int xid , struct cifs_io_parms * io_parms ,
unsigned int * nbytes , struct kvec * iov , int n_vec )
{
int rc = 0 ;
struct smb2_write_req * req = NULL ;
struct smb2_write_rsp * rsp = NULL ;
int resp_buftype ;
* nbytes = 0 ;
if ( n_vec < 1 )
return rc ;
rc = small_smb2_init ( SMB2_WRITE , io_parms - > tcon , ( void * * ) & req ) ;
if ( rc )
return rc ;
if ( io_parms - > tcon - > ses - > server = = NULL )
return - ECONNABORTED ;
req - > hdr . ProcessId = cpu_to_le32 ( io_parms - > pid ) ;
req - > PersistentFileId = io_parms - > persistent_fid ;
req - > VolatileFileId = io_parms - > volatile_fid ;
req - > WriteChannelInfoOffset = 0 ;
req - > WriteChannelInfoLength = 0 ;
req - > Channel = 0 ;
req - > Length = cpu_to_le32 ( io_parms - > length ) ;
req - > Offset = cpu_to_le64 ( io_parms - > offset ) ;
/* 4 for rfc1002 length field */
req - > DataOffset = cpu_to_le16 (
offsetof ( struct smb2_write_req , Buffer ) - 4 ) ;
req - > RemainingBytes = 0 ;
iov [ 0 ] . iov_base = ( char * ) req ;
/* 4 for rfc1002 length field and 1 for Buffer */
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 - 1 ;
/* length of entire message including data to be written */
inc_rfc1001_len ( req , io_parms - > length - 1 /* Buffer */ ) ;
rc = SendReceive2 ( xid , io_parms - > tcon - > ses , iov , n_vec + 1 ,
& resp_buftype , 0 ) ;
2012-09-19 16:03:26 +04:00
rsp = ( struct smb2_write_rsp * ) iov [ 0 ] . iov_base ;
2012-09-19 03:20:30 +04:00
if ( rc ) {
cifs_stats_fail_inc ( io_parms - > tcon , SMB2_WRITE_HE ) ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " Send error in write = %d \n " , rc ) ;
2012-09-19 16:03:26 +04:00
} else
2012-09-19 03:20:30 +04:00
* nbytes = le32_to_cpu ( rsp - > DataLength ) ;
2012-09-19 16:03:26 +04:00
free_rsp_buf ( resp_buftype , rsp ) ;
2012-09-19 03:20:30 +04:00
return rc ;
}
2012-09-19 03:20:31 +04:00
2012-09-19 03:20:33 +04:00
static unsigned int
num_entries ( char * bufstart , char * end_of_buf , char * * lastentry , size_t size )
{
int len ;
unsigned int entrycount = 0 ;
unsigned int next_offset = 0 ;
FILE_DIRECTORY_INFO * entryptr ;
if ( bufstart = = NULL )
return 0 ;
entryptr = ( FILE_DIRECTORY_INFO * ) bufstart ;
while ( 1 ) {
entryptr = ( FILE_DIRECTORY_INFO * )
( ( char * ) entryptr + next_offset ) ;
if ( ( char * ) entryptr + size > end_of_buf ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " malformed search entry would overflow \n " ) ;
2012-09-19 03:20:33 +04:00
break ;
}
len = le32_to_cpu ( entryptr - > FileNameLength ) ;
if ( ( char * ) entryptr + len + size > end_of_buf ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " directory entry name would overflow frame end of buf %p \n " ,
end_of_buf ) ;
2012-09-19 03:20:33 +04:00
break ;
}
* lastentry = ( char * ) entryptr ;
entrycount + + ;
next_offset = le32_to_cpu ( entryptr - > NextEntryOffset ) ;
if ( ! next_offset )
break ;
}
return entrycount ;
}
/*
* Readdir / FindFirst
*/
int
SMB2_query_directory ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , int index ,
struct cifs_search_info * srch_inf )
{
struct smb2_query_directory_req * req ;
struct smb2_query_directory_rsp * rsp = NULL ;
struct kvec iov [ 2 ] ;
int rc = 0 ;
int len ;
int resp_buftype ;
unsigned char * bufptr ;
struct TCP_Server_Info * server ;
struct cifs_ses * ses = tcon - > ses ;
__le16 asteriks = cpu_to_le16 ( ' * ' ) ;
char * end_of_smb ;
unsigned int output_size = CIFSMaxBufSize ;
size_t info_buf_size ;
if ( ses & & ( ses - > server ) )
server = ses - > server ;
else
return - EIO ;
rc = small_smb2_init ( SMB2_QUERY_DIRECTORY , tcon , ( void * * ) & req ) ;
if ( rc )
return rc ;
switch ( srch_inf - > info_level ) {
case SMB_FIND_FILE_DIRECTORY_INFO :
req - > FileInformationClass = FILE_DIRECTORY_INFORMATION ;
info_buf_size = sizeof ( FILE_DIRECTORY_INFO ) - 1 ;
break ;
case SMB_FIND_FILE_ID_FULL_DIR_INFO :
req - > FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION ;
info_buf_size = sizeof ( SEARCH_ID_FULL_DIR_INFO ) - 1 ;
break ;
default :
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " info level %u isn't supported \n " ,
srch_inf - > info_level ) ;
2012-09-19 03:20:33 +04:00
rc = - EINVAL ;
goto qdir_exit ;
}
req - > FileIndex = cpu_to_le32 ( index ) ;
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
len = 0x2 ;
bufptr = req - > Buffer ;
memcpy ( bufptr , & asteriks , len ) ;
req - > FileNameOffset =
cpu_to_le16 ( sizeof ( struct smb2_query_directory_req ) - 1 - 4 ) ;
req - > FileNameLength = cpu_to_le16 ( len ) ;
/*
* BB could be 30 bytes or so longer if we used SMB2 specific
* buffer lengths , but this is safe and close enough .
*/
output_size = min_t ( unsigned int , output_size , server - > maxBuf ) ;
output_size = min_t ( unsigned int , output_size , 2 < < 15 ) ;
req - > OutputBufferLength = cpu_to_le32 ( output_size ) ;
iov [ 0 ] . iov_base = ( char * ) req ;
/* 4 for RFC1001 length and 1 for Buffer */
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 - 1 ;
iov [ 1 ] . iov_base = ( char * ) ( req - > Buffer ) ;
iov [ 1 ] . iov_len = len ;
inc_rfc1001_len ( req , len - 1 /* Buffer */ ) ;
rc = SendReceive2 ( xid , ses , iov , 2 , & resp_buftype , 0 ) ;
2012-09-19 16:03:26 +04:00
rsp = ( struct smb2_query_directory_rsp * ) iov [ 0 ] . iov_base ;
2012-09-19 03:20:33 +04:00
if ( rc ) {
cifs_stats_fail_inc ( tcon , SMB2_QUERY_DIRECTORY_HE ) ;
goto qdir_exit ;
}
rc = validate_buf ( le16_to_cpu ( rsp - > OutputBufferOffset ) ,
le32_to_cpu ( rsp - > OutputBufferLength ) , & rsp - > hdr ,
info_buf_size ) ;
if ( rc )
goto qdir_exit ;
srch_inf - > unicode = true ;
if ( srch_inf - > ntwrk_buf_start ) {
if ( srch_inf - > smallBuf )
cifs_small_buf_release ( srch_inf - > ntwrk_buf_start ) ;
else
cifs_buf_release ( srch_inf - > ntwrk_buf_start ) ;
}
srch_inf - > ntwrk_buf_start = ( char * ) rsp ;
srch_inf - > srch_entries_start = srch_inf - > last_entry = 4 /* rfclen */ +
( char * ) & rsp - > hdr + le16_to_cpu ( rsp - > OutputBufferOffset ) ;
/* 4 for rfc1002 length field */
end_of_smb = get_rfc1002_length ( rsp ) + 4 + ( char * ) & rsp - > hdr ;
srch_inf - > entries_in_buffer =
num_entries ( srch_inf - > srch_entries_start , end_of_smb ,
& srch_inf - > last_entry , info_buf_size ) ;
srch_inf - > index_of_last_entry + = srch_inf - > entries_in_buffer ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " num entries %d last_index %lld srch start %p srch end %p \n " ,
srch_inf - > entries_in_buffer , srch_inf - > index_of_last_entry ,
srch_inf - > srch_entries_start , srch_inf - > last_entry ) ;
2012-09-19 03:20:33 +04:00
if ( resp_buftype = = CIFS_LARGE_BUFFER )
srch_inf - > smallBuf = false ;
else if ( resp_buftype = = CIFS_SMALL_BUFFER )
srch_inf - > smallBuf = true ;
else
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " illegal search buffer type \n " ) ;
2012-09-19 03:20:33 +04:00
if ( rsp - > hdr . Status = = STATUS_NO_MORE_FILES )
srch_inf - > endOfSearch = 1 ;
else
srch_inf - > endOfSearch = 0 ;
return rc ;
qdir_exit :
free_rsp_buf ( resp_buftype , rsp ) ;
return rc ;
}
2012-09-19 03:20:31 +04:00
static int
send_set_info ( const unsigned int xid , struct cifs_tcon * tcon ,
2012-09-19 03:20:32 +04:00
u64 persistent_fid , u64 volatile_fid , u32 pid , int info_class ,
2012-09-19 03:20:31 +04:00
unsigned int num , void * * data , unsigned int * size )
{
struct smb2_set_info_req * req ;
struct smb2_set_info_rsp * rsp = NULL ;
struct kvec * iov ;
int rc = 0 ;
int resp_buftype ;
unsigned int i ;
struct TCP_Server_Info * server ;
struct cifs_ses * ses = tcon - > ses ;
if ( ses & & ( ses - > server ) )
server = ses - > server ;
else
return - EIO ;
if ( ! num )
return - EINVAL ;
iov = kmalloc ( sizeof ( struct kvec ) * num , GFP_KERNEL ) ;
if ( ! iov )
return - ENOMEM ;
rc = small_smb2_init ( SMB2_SET_INFO , tcon , ( void * * ) & req ) ;
if ( rc ) {
kfree ( iov ) ;
return rc ;
}
2012-09-19 03:20:32 +04:00
req - > hdr . ProcessId = cpu_to_le32 ( pid ) ;
2012-09-19 03:20:31 +04:00
req - > InfoType = SMB2_O_INFO_FILE ;
req - > FileInfoClass = info_class ;
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
/* 4 for RFC1001 length and 1 for Buffer */
req - > BufferOffset =
cpu_to_le16 ( sizeof ( struct smb2_set_info_req ) - 1 - 4 ) ;
req - > BufferLength = cpu_to_le32 ( * size ) ;
inc_rfc1001_len ( req , * size - 1 /* Buffer */ ) ;
memcpy ( req - > Buffer , * data , * size ) ;
iov [ 0 ] . iov_base = ( char * ) req ;
/* 4 for RFC1001 length */
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 ;
for ( i = 1 ; i < num ; i + + ) {
inc_rfc1001_len ( req , size [ i ] ) ;
le32_add_cpu ( & req - > BufferLength , size [ i ] ) ;
iov [ i ] . iov_base = ( char * ) data [ i ] ;
iov [ i ] . iov_len = size [ i ] ;
}
rc = SendReceive2 ( xid , ses , iov , num , & resp_buftype , 0 ) ;
rsp = ( struct smb2_set_info_rsp * ) iov [ 0 ] . iov_base ;
2013-11-18 19:56:28 +04:00
if ( rc ! = 0 )
2012-09-19 03:20:31 +04:00
cifs_stats_fail_inc ( tcon , SMB2_SET_INFO_HE ) ;
2013-11-18 19:56:28 +04:00
2012-09-19 03:20:31 +04:00
free_rsp_buf ( resp_buftype , rsp ) ;
kfree ( iov ) ;
return rc ;
}
int
SMB2_rename ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , __le16 * target_file )
{
struct smb2_file_rename_info info ;
void * * data ;
unsigned int size [ 2 ] ;
int rc ;
int len = ( 2 * UniStrnlen ( ( wchar_t * ) target_file , PATH_MAX ) ) ;
data = kmalloc ( sizeof ( void * ) * 2 , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
info . ReplaceIfExists = 1 ; /* 1 = replace existing target with new */
/* 0 = fail if target already exists */
info . RootDirectory = 0 ; /* MBZ for network ops (why does spec say?) */
info . FileNameLength = cpu_to_le32 ( len ) ;
data [ 0 ] = & info ;
size [ 0 ] = sizeof ( struct smb2_file_rename_info ) ;
data [ 1 ] = target_file ;
size [ 1 ] = len + 2 /* null */ ;
rc = send_set_info ( xid , tcon , persistent_fid , volatile_fid ,
2012-09-19 03:20:32 +04:00
current - > tgid , FILE_RENAME_INFORMATION , 2 , data ,
size ) ;
2012-09-19 03:20:31 +04:00
kfree ( data ) ;
return rc ;
}
2012-09-19 03:20:31 +04:00
int
SMB2_set_hardlink ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , __le16 * target_file )
{
struct smb2_file_link_info info ;
void * * data ;
unsigned int size [ 2 ] ;
int rc ;
int len = ( 2 * UniStrnlen ( ( wchar_t * ) target_file , PATH_MAX ) ) ;
data = kmalloc ( sizeof ( void * ) * 2 , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
info . ReplaceIfExists = 0 ; /* 1 = replace existing link with new */
/* 0 = fail if link already exists */
info . RootDirectory = 0 ; /* MBZ for network ops (why does spec say?) */
info . FileNameLength = cpu_to_le32 ( len ) ;
data [ 0 ] = & info ;
size [ 0 ] = sizeof ( struct smb2_file_link_info ) ;
data [ 1 ] = target_file ;
size [ 1 ] = len + 2 /* null */ ;
rc = send_set_info ( xid , tcon , persistent_fid , volatile_fid ,
2012-09-19 03:20:32 +04:00
current - > tgid , FILE_LINK_INFORMATION , 2 , data , size ) ;
2012-09-19 03:20:31 +04:00
kfree ( data ) ;
return rc ;
}
2012-09-19 03:20:32 +04:00
int
SMB2_set_eof ( const unsigned int xid , struct cifs_tcon * tcon , u64 persistent_fid ,
u64 volatile_fid , u32 pid , __le64 * eof )
{
struct smb2_file_eof_info info ;
void * data ;
unsigned int size ;
info . EndOfFile = * eof ;
data = & info ;
size = sizeof ( struct smb2_file_eof_info ) ;
return send_set_info ( xid , tcon , persistent_fid , volatile_fid , pid ,
FILE_END_OF_FILE_INFORMATION , 1 , & data , & size ) ;
}
2012-09-19 03:20:32 +04:00
int
SMB2_set_info ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , FILE_BASIC_INFO * buf )
{
unsigned int size ;
size = sizeof ( FILE_BASIC_INFO ) ;
return send_set_info ( xid , tcon , persistent_fid , volatile_fid ,
current - > tgid , FILE_BASIC_INFORMATION , 1 ,
( void * * ) & buf , & size ) ;
}
2012-09-19 03:20:33 +04:00
int
SMB2_oplock_break ( const unsigned int xid , struct cifs_tcon * tcon ,
const u64 persistent_fid , const u64 volatile_fid ,
__u8 oplock_level )
{
int rc ;
struct smb2_oplock_break * req = NULL ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " SMB2_oplock_break \n " ) ;
2012-09-19 03:20:33 +04:00
rc = small_smb2_init ( SMB2_OPLOCK_BREAK , tcon , ( void * * ) & req ) ;
if ( rc )
return rc ;
req - > VolatileFid = volatile_fid ;
req - > PersistentFid = persistent_fid ;
req - > OplockLevel = oplock_level ;
req - > hdr . CreditRequest = cpu_to_le16 ( 1 ) ;
rc = SendReceiveNoRsp ( xid , tcon - > ses , ( char * ) req , CIFS_OBREAK_OP ) ;
/* SMB2 buffer freed by function above */
if ( rc ) {
cifs_stats_fail_inc ( tcon , SMB2_OPLOCK_BREAK_HE ) ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Send error in Oplock Break = %d \n " , rc ) ;
2012-09-19 03:20:33 +04:00
}
return rc ;
}
2012-09-19 03:20:34 +04:00
static void
copy_fs_info_to_kstatfs ( struct smb2_fs_full_size_info * pfs_inf ,
struct kstatfs * kst )
{
kst - > f_bsize = le32_to_cpu ( pfs_inf - > BytesPerSector ) *
le32_to_cpu ( pfs_inf - > SectorsPerAllocationUnit ) ;
kst - > f_blocks = le64_to_cpu ( pfs_inf - > TotalAllocationUnits ) ;
kst - > f_bfree = le64_to_cpu ( pfs_inf - > ActualAvailableAllocationUnits ) ;
kst - > f_bavail = le64_to_cpu ( pfs_inf - > CallerAvailableAllocationUnits ) ;
return ;
}
static int
build_qfs_info_req ( struct kvec * iov , struct cifs_tcon * tcon , int level ,
int outbuf_len , u64 persistent_fid , u64 volatile_fid )
{
int rc ;
struct smb2_query_info_req * req ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Query FSInfo level %d \n " , level ) ;
2012-09-19 03:20:34 +04:00
if ( ( tcon - > ses = = NULL ) | | ( tcon - > ses - > server = = NULL ) )
return - EIO ;
rc = small_smb2_init ( SMB2_QUERY_INFO , tcon , ( void * * ) & req ) ;
if ( rc )
return rc ;
req - > InfoType = SMB2_O_INFO_FILESYSTEM ;
req - > FileInfoClass = level ;
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
/* 4 for rfc1002 length field and 1 for pad */
req - > InputBufferOffset =
cpu_to_le16 ( sizeof ( struct smb2_query_info_req ) - 1 - 4 ) ;
req - > OutputBufferLength = cpu_to_le32 (
outbuf_len + sizeof ( struct smb2_query_info_rsp ) - 1 - 4 ) ;
iov - > iov_base = ( char * ) req ;
/* 4 for rfc1002 length field */
iov - > iov_len = get_rfc1002_length ( req ) + 4 ;
return 0 ;
}
int
SMB2_QFS_info ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , struct kstatfs * fsdata )
{
struct smb2_query_info_rsp * rsp = NULL ;
struct kvec iov ;
int rc = 0 ;
int resp_buftype ;
struct cifs_ses * ses = tcon - > ses ;
struct smb2_fs_full_size_info * info = NULL ;
rc = build_qfs_info_req ( & iov , tcon , FS_FULL_SIZE_INFORMATION ,
sizeof ( struct smb2_fs_full_size_info ) ,
persistent_fid , volatile_fid ) ;
if ( rc )
return rc ;
rc = SendReceive2 ( xid , ses , & iov , 1 , & resp_buftype , 0 ) ;
if ( rc ) {
cifs_stats_fail_inc ( tcon , SMB2_QUERY_INFO_HE ) ;
2013-10-09 11:07:00 +04:00
goto qfsinf_exit ;
2012-09-19 03:20:34 +04:00
}
rsp = ( struct smb2_query_info_rsp * ) iov . iov_base ;
info = ( struct smb2_fs_full_size_info * ) ( 4 /* RFC1001 len */ +
le16_to_cpu ( rsp - > OutputBufferOffset ) + ( char * ) & rsp - > hdr ) ;
rc = validate_buf ( le16_to_cpu ( rsp - > OutputBufferOffset ) ,
le32_to_cpu ( rsp - > OutputBufferLength ) , & rsp - > hdr ,
sizeof ( struct smb2_fs_full_size_info ) ) ;
if ( ! rc )
copy_fs_info_to_kstatfs ( info , fsdata ) ;
2013-10-09 11:07:00 +04:00
qfsinf_exit :
free_rsp_buf ( resp_buftype , iov . iov_base ) ;
return rc ;
}
int
SMB2_QFS_attr ( const unsigned int xid , struct cifs_tcon * tcon ,
2013-10-09 22:36:35 +04:00
u64 persistent_fid , u64 volatile_fid , int level )
2013-10-09 11:07:00 +04:00
{
struct smb2_query_info_rsp * rsp = NULL ;
struct kvec iov ;
int rc = 0 ;
2013-10-09 22:36:35 +04:00
int resp_buftype , max_len , min_len ;
2013-10-09 11:07:00 +04:00
struct cifs_ses * ses = tcon - > ses ;
unsigned int rsp_len , offset ;
2013-10-09 22:36:35 +04:00
if ( level = = FS_DEVICE_INFORMATION ) {
max_len = sizeof ( FILE_SYSTEM_DEVICE_INFO ) ;
min_len = sizeof ( FILE_SYSTEM_DEVICE_INFO ) ;
} else if ( level = = FS_ATTRIBUTE_INFORMATION ) {
max_len = sizeof ( FILE_SYSTEM_ATTRIBUTE_INFO ) ;
min_len = MIN_FS_ATTR_INFO_SIZE ;
2013-10-10 05:55:53 +04:00
} else if ( level = = FS_SECTOR_SIZE_INFORMATION ) {
max_len = sizeof ( struct smb3_fs_ss_info ) ;
min_len = sizeof ( struct smb3_fs_ss_info ) ;
2013-10-09 22:36:35 +04:00
} else {
2013-10-10 05:55:53 +04:00
cifs_dbg ( FYI , " Invalid qfsinfo level %d \n " , level ) ;
2013-10-09 22:36:35 +04:00
return - EINVAL ;
}
rc = build_qfs_info_req ( & iov , tcon , level , max_len ,
2013-10-09 11:07:00 +04:00
persistent_fid , volatile_fid ) ;
if ( rc )
return rc ;
rc = SendReceive2 ( xid , ses , & iov , 1 , & resp_buftype , 0 ) ;
if ( rc ) {
cifs_stats_fail_inc ( tcon , SMB2_QUERY_INFO_HE ) ;
goto qfsattr_exit ;
}
rsp = ( struct smb2_query_info_rsp * ) iov . iov_base ;
rsp_len = le32_to_cpu ( rsp - > OutputBufferLength ) ;
offset = le16_to_cpu ( rsp - > OutputBufferOffset ) ;
2013-10-09 22:36:35 +04:00
rc = validate_buf ( offset , rsp_len , & rsp - > hdr , min_len ) ;
if ( rc )
goto qfsattr_exit ;
if ( level = = FS_ATTRIBUTE_INFORMATION )
2013-10-09 11:07:00 +04:00
memcpy ( & tcon - > fsAttrInfo , 4 /* RFC1001 len */ + offset
+ ( char * ) & rsp - > hdr , min_t ( unsigned int ,
2013-10-09 22:36:35 +04:00
rsp_len , max_len ) ) ;
else if ( level = = FS_DEVICE_INFORMATION )
memcpy ( & tcon - > fsDevInfo , 4 /* RFC1001 len */ + offset
+ ( char * ) & rsp - > hdr , sizeof ( FILE_SYSTEM_DEVICE_INFO ) ) ;
2013-10-10 05:55:53 +04:00
else if ( level = = FS_SECTOR_SIZE_INFORMATION ) {
struct smb3_fs_ss_info * ss_info = ( struct smb3_fs_ss_info * )
( 4 /* RFC1001 len */ + offset + ( char * ) & rsp - > hdr ) ;
tcon - > ss_flags = le32_to_cpu ( ss_info - > Flags ) ;
tcon - > perf_sector_size =
le32_to_cpu ( ss_info - > PhysicalBytesPerSectorForPerf ) ;
}
2013-10-09 11:07:00 +04:00
qfsattr_exit :
2012-09-19 03:20:34 +04:00
free_rsp_buf ( resp_buftype , iov . iov_base ) ;
return rc ;
}
2012-09-19 17:22:43 +04:00
int
smb2_lockv ( const unsigned int xid , struct cifs_tcon * tcon ,
const __u64 persist_fid , const __u64 volatile_fid , const __u32 pid ,
const __u32 num_lock , struct smb2_lock_element * buf )
{
int rc = 0 ;
struct smb2_lock_req * req = NULL ;
struct kvec iov [ 2 ] ;
int resp_buf_type ;
unsigned int count ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " smb2_lockv num lock %d \n " , num_lock ) ;
2012-09-19 17:22:43 +04:00
rc = small_smb2_init ( SMB2_LOCK , tcon , ( void * * ) & req ) ;
if ( rc )
return rc ;
req - > hdr . ProcessId = cpu_to_le32 ( pid ) ;
req - > LockCount = cpu_to_le16 ( num_lock ) ;
req - > PersistentFileId = persist_fid ;
req - > VolatileFileId = volatile_fid ;
count = num_lock * sizeof ( struct smb2_lock_element ) ;
inc_rfc1001_len ( req , count - sizeof ( struct smb2_lock_element ) ) ;
iov [ 0 ] . iov_base = ( char * ) req ;
/* 4 for rfc1002 length field and count for all locks */
iov [ 0 ] . iov_len = get_rfc1002_length ( req ) + 4 - count ;
iov [ 1 ] . iov_base = ( char * ) buf ;
iov [ 1 ] . iov_len = count ;
cifs_stats_inc ( & tcon - > stats . cifs_stats . num_locks ) ;
rc = SendReceive2 ( xid , tcon - > ses , iov , 2 , & resp_buf_type , CIFS_NO_RESP ) ;
if ( rc ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Send error in smb2_lockv = %d \n " , rc ) ;
2012-09-19 17:22:43 +04:00
cifs_stats_fail_inc ( tcon , SMB2_LOCK_HE ) ;
}
return rc ;
}
int
SMB2_lock ( const unsigned int xid , struct cifs_tcon * tcon ,
const __u64 persist_fid , const __u64 volatile_fid , const __u32 pid ,
const __u64 length , const __u64 offset , const __u32 lock_flags ,
const bool wait )
{
struct smb2_lock_element lock ;
lock . Offset = cpu_to_le64 ( offset ) ;
lock . Length = cpu_to_le64 ( length ) ;
lock . Flags = cpu_to_le32 ( lock_flags ) ;
if ( ! wait & & lock_flags ! = SMB2_LOCKFLAG_UNLOCK )
lock . Flags | = cpu_to_le32 ( SMB2_LOCKFLAG_FAIL_IMMEDIATELY ) ;
return smb2_lockv ( xid , tcon , persist_fid , volatile_fid , pid , 1 , & lock ) ;
}
2012-09-19 17:22:45 +04:00
int
SMB2_lease_break ( const unsigned int xid , struct cifs_tcon * tcon ,
__u8 * lease_key , const __le32 lease_state )
{
int rc ;
struct smb2_lease_ack * req = NULL ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " SMB2_lease_break \n " ) ;
2012-09-19 17:22:45 +04:00
rc = small_smb2_init ( SMB2_OPLOCK_BREAK , tcon , ( void * * ) & req ) ;
if ( rc )
return rc ;
req - > hdr . CreditRequest = cpu_to_le16 ( 1 ) ;
req - > StructureSize = cpu_to_le16 ( 36 ) ;
inc_rfc1001_len ( req , 12 ) ;
memcpy ( req - > LeaseKey , lease_key , 16 ) ;
req - > LeaseState = lease_state ;
rc = SendReceiveNoRsp ( xid , tcon - > ses , ( char * ) req , CIFS_OBREAK_OP ) ;
/* SMB2 buffer freed by function above */
if ( rc ) {
cifs_stats_fail_inc ( tcon , SMB2_OPLOCK_BREAK_HE ) ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Send error in Lease Break = %d \n " , rc ) ;
2012-09-19 17:22:45 +04:00
}
return rc ;
}