2011-02-24 21:07:19 +03:00
/*
* SMB2 version specific operations
*
* Copyright ( c ) 2012 , Jeff Layton < jlayton @ redhat . com >
*
* This library is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License v2 as published
* by the Free Software Foundation .
*
* 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
*/
2012-09-19 03:20:28 +04:00
# include <linux/pagemap.h>
2011-02-24 21:07:19 +03:00
# include "cifsglob.h"
2011-12-26 22:53:34 +04:00
# include "smb2pdu.h"
# include "smb2proto.h"
2012-05-23 16:18:00 +04:00
# include "cifsproto.h"
# include "cifs_debug.h"
static int
change_conf ( struct TCP_Server_Info * server )
{
server - > credits + = server - > echo_credits + server - > oplock_credits ;
server - > oplock_credits = server - > echo_credits = 0 ;
switch ( server - > credits ) {
case 0 :
return - 1 ;
case 1 :
server - > echoes = false ;
server - > oplocks = false ;
cERROR ( 1 , " disabling echoes and oplocks " ) ;
break ;
case 2 :
server - > echoes = true ;
server - > oplocks = false ;
server - > echo_credits = 1 ;
cFYI ( 1 , " disabling oplocks " ) ;
break ;
default :
server - > echoes = true ;
server - > oplocks = true ;
server - > echo_credits = 1 ;
server - > oplock_credits = 1 ;
}
server - > credits - = server - > echo_credits + server - > oplock_credits ;
return 0 ;
}
static void
smb2_add_credits ( struct TCP_Server_Info * server , const unsigned int add ,
const int optype )
{
int * val , rc = 0 ;
spin_lock ( & server - > req_lock ) ;
val = server - > ops - > get_credits_field ( server , optype ) ;
* val + = add ;
server - > in_flight - - ;
2011-12-27 16:12:43 +04:00
if ( server - > in_flight = = 0 & & ( optype & CIFS_OP_MASK ) ! = CIFS_NEG_OP )
2012-05-23 16:18:00 +04:00
rc = change_conf ( server ) ;
spin_unlock ( & server - > req_lock ) ;
wake_up ( & server - > request_q ) ;
if ( rc )
cifs_reconnect ( server ) ;
}
static void
smb2_set_credits ( struct TCP_Server_Info * server , const int val )
{
spin_lock ( & server - > req_lock ) ;
server - > credits = val ;
spin_unlock ( & server - > req_lock ) ;
}
static int *
smb2_get_credits_field ( struct TCP_Server_Info * server , const int optype )
{
switch ( optype ) {
case CIFS_ECHO_OP :
return & server - > echo_credits ;
case CIFS_OBREAK_OP :
return & server - > oplock_credits ;
default :
return & server - > credits ;
}
}
static unsigned int
smb2_get_credits ( struct mid_q_entry * mid )
{
return le16_to_cpu ( ( ( struct smb2_hdr * ) mid - > resp_buf ) - > CreditRequest ) ;
}
2011-12-26 22:53:34 +04:00
static __u64
smb2_get_next_mid ( struct TCP_Server_Info * server )
{
__u64 mid ;
/* for SMB2 we need the current value */
spin_lock ( & GlobalMid_Lock ) ;
mid = server - > CurrentMid + + ;
spin_unlock ( & GlobalMid_Lock ) ;
return mid ;
}
2011-02-24 21:07:19 +03:00
2011-06-08 15:51:07 +04:00
static struct mid_q_entry *
smb2_find_mid ( struct TCP_Server_Info * server , char * buf )
{
struct mid_q_entry * mid ;
struct smb2_hdr * hdr = ( struct smb2_hdr * ) buf ;
spin_lock ( & GlobalMid_Lock ) ;
list_for_each_entry ( mid , & server - > pending_mid_q , qhead ) {
if ( ( mid - > mid = = hdr - > MessageId ) & &
( mid - > mid_state = = MID_REQUEST_SUBMITTED ) & &
( mid - > command = = hdr - > Command ) ) {
spin_unlock ( & GlobalMid_Lock ) ;
return mid ;
}
}
spin_unlock ( & GlobalMid_Lock ) ;
return NULL ;
}
static void
smb2_dump_detail ( void * buf )
{
# ifdef CONFIG_CIFS_DEBUG2
struct smb2_hdr * smb = ( struct smb2_hdr * ) buf ;
cERROR ( 1 , " Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d " ,
smb - > Command , smb - > Status , smb - > Flags , smb - > MessageId ,
smb - > ProcessId ) ;
cERROR ( 1 , " smb buf %p len %u " , smb , smb2_calc_size ( smb ) ) ;
# endif
}
2011-12-27 16:12:43 +04:00
static bool
smb2_need_neg ( struct TCP_Server_Info * server )
{
return server - > max_read = = 0 ;
}
static int
smb2_negotiate ( const unsigned int xid , struct cifs_ses * ses )
{
int rc ;
ses - > server - > CurrentMid = 0 ;
rc = SMB2_negotiate ( xid , ses ) ;
/* BB we probably don't need to retry with modern servers */
if ( rc = = - EAGAIN )
rc = - EHOSTDOWN ;
return rc ;
}
2012-09-19 03:20:28 +04:00
static unsigned int
smb2_negotiate_wsize ( struct cifs_tcon * tcon , struct smb_vol * volume_info )
{
struct TCP_Server_Info * server = tcon - > ses - > server ;
unsigned int wsize ;
/* start with specified wsize, or default */
wsize = volume_info - > wsize ? volume_info - > wsize : CIFS_DEFAULT_IOSIZE ;
wsize = min_t ( unsigned int , wsize , server - > max_write ) ;
/*
* limit write size to 2 * * 16 , because we don ' t support multicredit
* requests now .
*/
wsize = min_t ( unsigned int , wsize , 2 < < 15 ) ;
/* limit to the amount that we can kmap at once */
wsize = min_t ( unsigned int , wsize , CIFS_KMAP_SIZE_LIMIT ) ;
return wsize ;
}
static unsigned int
smb2_negotiate_rsize ( struct cifs_tcon * tcon , struct smb_vol * volume_info )
{
struct TCP_Server_Info * server = tcon - > ses - > server ;
unsigned int rsize ;
/* start with specified rsize, or default */
rsize = volume_info - > rsize ? volume_info - > rsize : CIFS_DEFAULT_IOSIZE ;
rsize = min_t ( unsigned int , rsize , server - > max_read ) ;
/*
* limit write size to 2 * * 16 , because we don ' t support multicredit
* requests now .
*/
rsize = min_t ( unsigned int , rsize , 2 < < 15 ) ;
/* limit to the amount that we can kmap at once */
rsize = min_t ( unsigned int , rsize , CIFS_KMAP_SIZE_LIMIT ) ;
return rsize ;
}
2011-12-26 22:58:46 +04:00
static int
smb2_is_path_accessible ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifs_sb_info * cifs_sb , const char * full_path )
{
int rc ;
__u64 persistent_fid , volatile_fid ;
__le16 * utf16_path ;
utf16_path = cifs_convert_path_to_utf16 ( full_path , cifs_sb ) ;
if ( ! utf16_path )
return - ENOMEM ;
rc = SMB2_open ( xid , tcon , utf16_path , & persistent_fid , & volatile_fid ,
2012-09-19 03:20:26 +04:00
FILE_READ_ATTRIBUTES , FILE_OPEN , 0 , 0 , NULL ) ;
2011-12-26 22:58:46 +04:00
if ( rc ) {
kfree ( utf16_path ) ;
return rc ;
}
rc = SMB2_close ( xid , tcon , persistent_fid , volatile_fid ) ;
kfree ( utf16_path ) ;
return rc ;
}
2011-12-29 17:06:33 +04:00
static int
smb2_get_srv_inum ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifs_sb_info * cifs_sb , const char * full_path ,
u64 * uniqueid , FILE_ALL_INFO * data )
{
* uniqueid = le64_to_cpu ( data - > IndexNumber ) ;
return 0 ;
}
2012-09-19 03:20:27 +04:00
static int
smb2_query_file_info ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifs_fid * fid , FILE_ALL_INFO * data )
{
int rc ;
struct smb2_file_all_info * smb2_data ;
smb2_data = kzalloc ( sizeof ( struct smb2_file_all_info ) + MAX_NAME * 2 ,
GFP_KERNEL ) ;
if ( smb2_data = = NULL )
return - ENOMEM ;
rc = SMB2_query_info ( xid , tcon , fid - > persistent_fid , fid - > volatile_fid ,
smb2_data ) ;
if ( ! rc )
move_smb2_info_to_cifs ( data , smb2_data ) ;
kfree ( smb2_data ) ;
return rc ;
}
2012-05-27 20:44:23 +04:00
static char *
smb2_build_path_to_root ( struct smb_vol * vol , struct cifs_sb_info * cifs_sb ,
struct cifs_tcon * tcon )
{
int pplen = vol - > prepath ? strlen ( vol - > prepath ) : 0 ;
char * full_path = NULL ;
/* if no prefix path, simply set path to the root of share to "" */
if ( pplen = = 0 ) {
full_path = kzalloc ( 2 , GFP_KERNEL ) ;
return full_path ;
}
cERROR ( 1 , " prefixpath is not supported for SMB2 now " ) ;
return NULL ;
}
2012-07-12 18:30:44 +04:00
static bool
smb2_can_echo ( struct TCP_Server_Info * server )
{
return server - > echoes ;
}
2012-05-28 15:19:39 +04:00
static void
smb2_clear_stats ( struct cifs_tcon * tcon )
{
# ifdef CONFIG_CIFS_STATS
int i ;
for ( i = 0 ; i < NUMBER_OF_SMB2_COMMANDS ; i + + ) {
atomic_set ( & tcon - > stats . smb2_stats . smb2_com_sent [ i ] , 0 ) ;
atomic_set ( & tcon - > stats . smb2_stats . smb2_com_failed [ i ] , 0 ) ;
}
# endif
}
static void
smb2_print_stats ( struct seq_file * m , struct cifs_tcon * tcon )
{
# ifdef CONFIG_CIFS_STATS
atomic_t * sent = tcon - > stats . smb2_stats . smb2_com_sent ;
atomic_t * failed = tcon - > stats . smb2_stats . smb2_com_failed ;
seq_printf ( m , " \n Negotiates: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_NEGOTIATE_HE ] ) ,
atomic_read ( & failed [ SMB2_NEGOTIATE_HE ] ) ) ;
seq_printf ( m , " \n SessionSetups: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_SESSION_SETUP_HE ] ) ,
atomic_read ( & failed [ SMB2_SESSION_SETUP_HE ] ) ) ;
# define SMB2LOGOFF 0x0002 /* trivial request/resp */
seq_printf ( m , " \n Logoffs: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_LOGOFF_HE ] ) ,
atomic_read ( & failed [ SMB2_LOGOFF_HE ] ) ) ;
seq_printf ( m , " \n TreeConnects: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_TREE_CONNECT_HE ] ) ,
atomic_read ( & failed [ SMB2_TREE_CONNECT_HE ] ) ) ;
seq_printf ( m , " \n TreeDisconnects: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_TREE_DISCONNECT_HE ] ) ,
atomic_read ( & failed [ SMB2_TREE_DISCONNECT_HE ] ) ) ;
seq_printf ( m , " \n Creates: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_CREATE_HE ] ) ,
atomic_read ( & failed [ SMB2_CREATE_HE ] ) ) ;
seq_printf ( m , " \n Closes: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_CLOSE_HE ] ) ,
atomic_read ( & failed [ SMB2_CLOSE_HE ] ) ) ;
seq_printf ( m , " \n Flushes: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_FLUSH_HE ] ) ,
atomic_read ( & failed [ SMB2_FLUSH_HE ] ) ) ;
seq_printf ( m , " \n Reads: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_READ_HE ] ) ,
atomic_read ( & failed [ SMB2_READ_HE ] ) ) ;
seq_printf ( m , " \n Writes: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_WRITE_HE ] ) ,
atomic_read ( & failed [ SMB2_WRITE_HE ] ) ) ;
seq_printf ( m , " \n Locks: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_LOCK_HE ] ) ,
atomic_read ( & failed [ SMB2_LOCK_HE ] ) ) ;
seq_printf ( m , " \n IOCTLs: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_IOCTL_HE ] ) ,
atomic_read ( & failed [ SMB2_IOCTL_HE ] ) ) ;
seq_printf ( m , " \n Cancels: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_CANCEL_HE ] ) ,
atomic_read ( & failed [ SMB2_CANCEL_HE ] ) ) ;
seq_printf ( m , " \n Echos: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_ECHO_HE ] ) ,
atomic_read ( & failed [ SMB2_ECHO_HE ] ) ) ;
seq_printf ( m , " \n QueryDirectories: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_QUERY_DIRECTORY_HE ] ) ,
atomic_read ( & failed [ SMB2_QUERY_DIRECTORY_HE ] ) ) ;
seq_printf ( m , " \n ChangeNotifies: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_CHANGE_NOTIFY_HE ] ) ,
atomic_read ( & failed [ SMB2_CHANGE_NOTIFY_HE ] ) ) ;
seq_printf ( m , " \n QueryInfos: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_QUERY_INFO_HE ] ) ,
atomic_read ( & failed [ SMB2_QUERY_INFO_HE ] ) ) ;
seq_printf ( m , " \n SetInfos: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_SET_INFO_HE ] ) ,
atomic_read ( & failed [ SMB2_SET_INFO_HE ] ) ) ;
seq_printf ( m , " \n OplockBreaks: %d sent %d failed " ,
atomic_read ( & sent [ SMB2_OPLOCK_BREAK_HE ] ) ,
atomic_read ( & failed [ SMB2_OPLOCK_BREAK_HE ] ) ) ;
# endif
}
2012-09-19 03:20:26 +04:00
static void
smb2_set_fid ( struct cifsFileInfo * cfile , struct cifs_fid * fid , __u32 oplock )
{
/* struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); */
cfile - > fid . persistent_fid = fid - > persistent_fid ;
cfile - > fid . volatile_fid = fid - > volatile_fid ;
/* cifs_set_oplock_level(cinode, oplock); */
/* cinode->can_cache_brlcks = cinode->clientCanCacheAll; */
}
static int
smb2_close_file ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifs_fid * fid )
{
return SMB2_close ( xid , tcon , fid - > persistent_fid , fid - > volatile_fid ) ;
}
2012-09-19 03:20:28 +04:00
static int
smb2_flush_file ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifs_fid * fid )
{
return SMB2_flush ( xid , tcon , fid - > persistent_fid , fid - > volatile_fid ) ;
}
2012-09-19 03:20:29 +04:00
static unsigned int
smb2_read_data_offset ( char * buf )
{
struct smb2_read_rsp * rsp = ( struct smb2_read_rsp * ) buf ;
return rsp - > DataOffset ;
}
static unsigned int
smb2_read_data_length ( char * buf )
{
struct smb2_read_rsp * rsp = ( struct smb2_read_rsp * ) buf ;
return le32_to_cpu ( rsp - > DataLength ) ;
}
2012-09-19 03:20:30 +04:00
static int
smb2_sync_read ( const unsigned int xid , struct cifsFileInfo * cfile ,
struct cifs_io_parms * parms , unsigned int * bytes_read ,
char * * buf , int * buf_type )
{
parms - > persistent_fid = cfile - > fid . persistent_fid ;
parms - > volatile_fid = cfile - > fid . volatile_fid ;
return SMB2_read ( xid , parms , bytes_read , buf , buf_type ) ;
}
2012-09-19 03:20:30 +04:00
static int
smb2_sync_write ( const unsigned int xid , struct cifsFileInfo * cfile ,
struct cifs_io_parms * parms , unsigned int * written ,
struct kvec * iov , unsigned long nr_segs )
{
parms - > persistent_fid = cfile - > fid . persistent_fid ;
parms - > volatile_fid = cfile - > fid . volatile_fid ;
return SMB2_write ( xid , parms , written , iov , nr_segs ) ;
}
2011-02-24 21:07:19 +03:00
struct smb_version_operations smb21_operations = {
2011-12-26 22:53:34 +04:00
. setup_request = smb2_setup_request ,
2012-07-11 14:45:28 +04:00
. setup_async_request = smb2_setup_async_request ,
2011-12-26 22:53:34 +04:00
. check_receive = smb2_check_receive ,
2012-05-23 16:18:00 +04:00
. add_credits = smb2_add_credits ,
. set_credits = smb2_set_credits ,
. get_credits_field = smb2_get_credits_field ,
. get_credits = smb2_get_credits ,
2011-12-26 22:53:34 +04:00
. get_next_mid = smb2_get_next_mid ,
2012-09-19 03:20:29 +04:00
. read_data_offset = smb2_read_data_offset ,
. read_data_length = smb2_read_data_length ,
. map_error = map_smb2_to_linux_error ,
2011-06-08 15:51:07 +04:00
. find_mid = smb2_find_mid ,
. check_message = smb2_check_message ,
. dump_detail = smb2_dump_detail ,
2012-05-28 15:19:39 +04:00
. clear_stats = smb2_clear_stats ,
. print_stats = smb2_print_stats ,
2011-12-27 16:12:43 +04:00
. need_neg = smb2_need_neg ,
. negotiate = smb2_negotiate ,
2012-09-19 03:20:28 +04:00
. negotiate_wsize = smb2_negotiate_wsize ,
. negotiate_rsize = smb2_negotiate_rsize ,
2011-12-27 16:22:00 +04:00
. sess_setup = SMB2_sess_setup ,
. logoff = SMB2_logoff ,
2011-12-27 16:04:00 +04:00
. tree_connect = SMB2_tcon ,
. tree_disconnect = SMB2_tdis ,
2011-12-26 22:58:46 +04:00
. is_path_accessible = smb2_is_path_accessible ,
2012-07-12 18:30:44 +04:00
. can_echo = smb2_can_echo ,
. echo = SMB2_echo ,
2011-12-29 17:06:33 +04:00
. query_path_info = smb2_query_path_info ,
. get_srv_inum = smb2_get_srv_inum ,
2012-09-19 03:20:27 +04:00
. query_file_info = smb2_query_file_info ,
2012-05-27 20:44:23 +04:00
. build_path_to_root = smb2_build_path_to_root ,
2011-07-19 12:56:37 +04:00
. mkdir = smb2_mkdir ,
. mkdir_setinfo = smb2_mkdir_setinfo ,
2012-07-10 16:14:38 +04:00
. rmdir = smb2_rmdir ,
2012-09-19 03:20:25 +04:00
. unlink = smb2_unlink ,
2012-09-19 03:20:31 +04:00
. rename = smb2_rename_path ,
2012-09-19 03:20:31 +04:00
. create_hardlink = smb2_create_hardlink ,
2012-09-19 03:20:26 +04:00
. open = smb2_open_file ,
. set_fid = smb2_set_fid ,
. close = smb2_close_file ,
2012-09-19 03:20:28 +04:00
. flush = smb2_flush_file ,
2012-09-19 03:20:29 +04:00
. async_readv = smb2_async_readv ,
2012-09-19 03:20:29 +04:00
. async_writev = smb2_async_writev ,
2012-09-19 03:20:30 +04:00
. sync_read = smb2_sync_read ,
2012-09-19 03:20:30 +04:00
. sync_write = smb2_sync_write ,
2011-02-24 21:07:19 +03:00
} ;
struct smb_version_values smb21_values = {
. version_string = SMB21_VERSION_STRING ,
2011-06-08 15:51:07 +04:00
. header_size = sizeof ( struct smb2_hdr ) ,
. max_header_size = MAX_SMB2_HDR_SIZE ,
2012-09-19 03:20:29 +04:00
. read_rsp_size = sizeof ( struct smb2_read_rsp ) - 1 ,
2011-12-26 22:53:34 +04:00
. lock_cmd = SMB2_LOCK ,
2012-07-13 13:58:14 +04:00
. cap_unix = 0 ,
. cap_nt_find = SMB2_NT_FIND ,
. cap_large_files = SMB2_LARGE_FILES ,
2011-02-24 21:07:19 +03:00
} ;