2019-05-08 22:36:25 +03:00
// SPDX-License-Identifier: GPL-2.0
2011-02-24 21:07:19 +03:00
/*
* SMB2 version specific operations
*
* Copyright ( c ) 2012 , Jeff Layton < jlayton @ redhat . com >
*/
2012-09-19 03:20:28 +04:00
# include <linux/pagemap.h>
2012-09-19 03:20:34 +04:00
# include <linux/vfs.h>
2014-07-20 06:44:58 +04:00
# include <linux/falloc.h>
2016-11-04 02:47:37 +03:00
# include <linux/scatterlist.h>
2017-03-30 13:34:14 +03:00
# include <linux/uuid.h>
2016-11-04 02:47:37 +03:00
# include <crypto/aead.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"
2013-08-14 19:25:21 +04:00
# include "cifs_unicode.h"
2012-09-19 03:20:33 +04:00
# include "smb2status.h"
2012-09-19 03:20:34 +04:00
# include "smb2glob.h"
2016-10-01 05:14:26 +03:00
# include "cifs_ioctl.h"
2017-11-23 03:38:39 +03:00
# include "smbdirect.h"
2012-05-23 16:18:00 +04:00
2019-01-19 04:25:36 +03:00
/* Change credits for different ops and return the total number of credits */
2012-05-23 16:18:00 +04:00
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 :
2019-01-19 04:25:36 +03:00
return 0 ;
2012-05-23 16:18:00 +04:00
case 1 :
server - > echoes = false ;
server - > oplocks = false ;
break ;
case 2 :
server - > echoes = true ;
server - > oplocks = false ;
server - > echo_credits = 1 ;
break ;
default :
server - > echoes = true ;
2015-09-22 17:29:38 +03:00
if ( enable_oplocks ) {
server - > oplocks = true ;
server - > oplock_credits = 1 ;
} else
server - > oplocks = false ;
2012-05-23 16:18:00 +04:00
server - > echo_credits = 1 ;
}
server - > credits - = server - > echo_credits + server - > oplock_credits ;
2019-01-19 04:25:36 +03:00
return server - > credits + server - > echo_credits + server - > oplock_credits ;
2012-05-23 16:18:00 +04:00
}
static void
2019-01-16 22:12:41 +03:00
smb2_add_credits ( struct TCP_Server_Info * server ,
const struct cifs_credits * credits , const int optype )
2012-05-23 16:18:00 +04:00
{
2019-01-19 04:25:36 +03:00
int * val , rc = - 1 ;
2019-01-16 22:12:41 +03:00
unsigned int add = credits - > value ;
unsigned int instance = credits - > instance ;
bool reconnect_detected = false ;
2019-01-19 04:25:36 +03:00
2012-05-23 16:18:00 +04:00
spin_lock ( & server - > req_lock ) ;
val = server - > ops - > get_credits_field ( server , optype ) ;
2018-09-01 09:10:17 +03:00
/* eg found case where write overlapping reconnect messed up credits */
if ( ( ( optype & CIFS_OP_MASK ) = = CIFS_NEG_OP ) & & ( * val ! = 0 ) )
trace_smb3_reconnect_with_invalid_credits ( server - > CurrentMid ,
server - > hostname , * val ) ;
2019-01-16 22:12:41 +03:00
if ( ( instance = = 0 ) | | ( instance = = server - > reconnect_instance ) )
* val + = add ;
else
reconnect_detected = true ;
2018-09-01 09:10:17 +03:00
2016-09-23 08:44:16 +03:00
if ( * val > 65000 ) {
* val = 65000 ; /* Don't get near 64K credits, avoid srv bugs */
printk_once ( KERN_WARNING " server overflowed SMB3 credits \n " ) ;
}
2012-05-23 16:18:00 +04:00
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 ) ;
2012-09-19 03:20:33 +04:00
/*
* Sometimes server returns 0 credits on oplock break ack - we need to
* rebalance credits in this case .
*/
else if ( server - > in_flight > 0 & & server - > oplock_credits = = 0 & &
server - > oplocks ) {
if ( server - > credits > 1 ) {
server - > credits - - ;
server - > oplock_credits + + ;
}
}
2012-05-23 16:18:00 +04:00
spin_unlock ( & server - > req_lock ) ;
wake_up ( & server - > request_q ) ;
2019-01-19 04:25:36 +03:00
2019-01-16 22:12:41 +03:00
if ( reconnect_detected )
cifs_dbg ( FYI , " trying to put %d credits from the old server instance %d \n " ,
add , instance ) ;
2019-01-25 21:56:41 +03:00
if ( server - > tcpStatus = = CifsNeedReconnect
| | server - > tcpStatus = = CifsExiting )
2019-01-19 04:25:36 +03:00
return ;
switch ( rc ) {
case - 1 :
/* change_conf hasn't been executed */
break ;
case 0 :
cifs_dbg ( VFS , " Possible client or server bug - zero credits \n " ) ;
break ;
case 1 :
cifs_dbg ( VFS , " disabling echoes and oplocks \n " ) ;
break ;
case 2 :
cifs_dbg ( FYI , " disabling oplocks \n " ) ;
break ;
default :
cifs_dbg ( FYI , " add %u credits total=%d \n " , add , rc ) ;
}
2012-05-23 16:18:00 +04:00
}
static void
smb2_set_credits ( struct TCP_Server_Info * server , const int val )
{
spin_lock ( & server - > req_lock ) ;
server - > credits = val ;
2018-09-19 10:38:17 +03:00
if ( val = = 1 )
server - > reconnect_instance + + ;
2012-05-23 16:18:00 +04:00
spin_unlock ( & server - > req_lock ) ;
2018-09-22 19:25:04 +03:00
/* don't log while holding the lock */
if ( val = = 1 )
cifs_dbg ( FYI , " set credits to 1 due to smb2 reconnect \n " ) ;
2012-05-23 16:18:00 +04:00
}
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 )
{
2018-06-01 03:53:06 +03:00
struct smb2_sync_hdr * shdr = ( struct smb2_sync_hdr * ) mid - > resp_buf ;
2016-10-25 01:33:04 +03:00
2019-01-23 03:50:21 +03:00
if ( mid - > mid_state = = MID_RESPONSE_RECEIVED
| | mid - > mid_state = = MID_RESPONSE_MALFORMED )
return le16_to_cpu ( shdr - > CreditRequest ) ;
return 0 ;
2012-05-23 16:18:00 +04:00
}
2011-12-26 22:53:34 +04:00
2014-06-05 19:03:27 +04:00
static int
smb2_wait_mtu_credits ( struct TCP_Server_Info * server , unsigned int size ,
2019-01-16 22:12:41 +03:00
unsigned int * num , struct cifs_credits * credits )
2014-06-05 19:03:27 +04:00
{
int rc = 0 ;
unsigned int scredits ;
spin_lock ( & server - > req_lock ) ;
while ( 1 ) {
if ( server - > credits < = 0 ) {
spin_unlock ( & server - > req_lock ) ;
cifs_num_waiters_inc ( server ) ;
rc = wait_event_killable ( server - > request_q ,
2019-03-08 05:58:20 +03:00
has_credits ( server , & server - > credits , 1 ) ) ;
2014-06-05 19:03:27 +04:00
cifs_num_waiters_dec ( server ) ;
if ( rc )
return rc ;
spin_lock ( & server - > req_lock ) ;
} else {
if ( server - > tcpStatus = = CifsExiting ) {
spin_unlock ( & server - > req_lock ) ;
return - ENOENT ;
}
scredits = server - > credits ;
/* can deadlock with reopen */
2019-01-17 19:21:24 +03:00
if ( scredits < = 8 ) {
2014-06-05 19:03:27 +04:00
* num = SMB2_MAX_BUFFER_SIZE ;
2019-01-16 22:12:41 +03:00
credits - > value = 0 ;
credits - > instance = 0 ;
2014-06-05 19:03:27 +04:00
break ;
}
2019-01-17 19:21:24 +03:00
/* leave some credits for reopen and other ops */
scredits - = 8 ;
2014-06-05 19:03:27 +04:00
* num = min_t ( unsigned int , size ,
scredits * SMB2_MAX_BUFFER_SIZE ) ;
2019-01-16 22:12:41 +03:00
credits - > value =
DIV_ROUND_UP ( * num , SMB2_MAX_BUFFER_SIZE ) ;
credits - > instance = server - > reconnect_instance ;
server - > credits - = credits - > value ;
2014-06-05 19:03:27 +04:00
server - > in_flight + + ;
break ;
}
}
spin_unlock ( & server - > req_lock ) ;
return rc ;
}
2019-01-24 05:15:52 +03:00
static int
smb2_adjust_credits ( struct TCP_Server_Info * server ,
struct cifs_credits * credits ,
const unsigned int payload_size )
{
int new_val = DIV_ROUND_UP ( payload_size , SMB2_MAX_BUFFER_SIZE ) ;
if ( ! credits - > value | | credits - > value = = new_val )
return 0 ;
if ( credits - > value < new_val ) {
WARN_ONCE ( 1 , " request has less credits (%d) than required (%d) " ,
credits - > value , new_val ) ;
return - ENOTSUPP ;
}
spin_lock ( & server - > req_lock ) ;
if ( server - > reconnect_instance ! = credits - > instance ) {
spin_unlock ( & server - > req_lock ) ;
cifs_dbg ( VFS , " trying to return %d credits to old session \n " ,
credits - > value - new_val ) ;
return - EAGAIN ;
}
server - > credits + = credits - > value - new_val ;
spin_unlock ( & server - > req_lock ) ;
wake_up ( & server - > request_q ) ;
credits - > value = new_val ;
return 0 ;
}
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
2019-03-05 01:02:50 +03:00
static void
smb2_revert_current_mid ( struct TCP_Server_Info * server , const unsigned int val )
{
spin_lock ( & GlobalMid_Lock ) ;
if ( server - > CurrentMid > = val )
server - > CurrentMid - = val ;
spin_unlock ( & GlobalMid_Lock ) ;
}
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 ;
2018-06-01 03:53:06 +03:00
struct smb2_sync_hdr * shdr = ( struct smb2_sync_hdr * ) buf ;
2016-10-25 01:33:04 +03:00
__u64 wire_mid = le64_to_cpu ( shdr - > MessageId ) ;
2011-06-08 15:51:07 +04:00
2016-10-25 01:33:04 +03:00
if ( shdr - > ProtocolId = = SMB2_TRANSFORM_PROTO_NUM ) {
2019-05-08 22:36:25 +03:00
cifs_dbg ( VFS , " Encrypted frame parsing not supported yet \n " ) ;
2015-12-18 22:05:30 +03:00
return NULL ;
}
2011-06-08 15:51:07 +04:00
spin_lock ( & GlobalMid_Lock ) ;
list_for_each_entry ( mid , & server - > pending_mid_q , qhead ) {
2014-12-09 20:37:00 +03:00
if ( ( mid - > mid = = wire_mid ) & &
2011-06-08 15:51:07 +04:00
( mid - > mid_state = = MID_REQUEST_SUBMITTED ) & &
2016-10-25 01:33:04 +03:00
( mid - > command = = shdr - > Command ) ) {
2018-06-25 15:05:25 +03:00
kref_get ( & mid - > refcount ) ;
2011-06-08 15:51:07 +04:00
spin_unlock ( & GlobalMid_Lock ) ;
return mid ;
}
}
spin_unlock ( & GlobalMid_Lock ) ;
return NULL ;
}
static void
2018-04-22 23:45:53 +03:00
smb2_dump_detail ( void * buf , struct TCP_Server_Info * server )
2011-06-08 15:51:07 +04:00
{
# ifdef CONFIG_CIFS_DEBUG2
2018-06-01 03:53:06 +03:00
struct smb2_sync_hdr * shdr = ( struct smb2_sync_hdr * ) buf ;
2011-06-08 15:51:07 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d \n " ,
2016-10-25 01:33:04 +03:00
shdr - > Command , shdr - > Status , shdr - > Flags , shdr - > MessageId ,
shdr - > ProcessId ) ;
2018-04-22 23:45:53 +03:00
cifs_dbg ( VFS , " smb buf %p len %u \n " , buf ,
2018-05-06 23:58:51 +03:00
server - > ops - > calc_smb_size ( buf , server ) ) ;
2011-06-08 15:51:07 +04:00
# 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 ;
2019-05-08 22:36:25 +03:00
2011-12-27 16:12:43 +04:00
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 ) ;
2017-11-23 03:38:39 +03:00
# ifdef CONFIG_CIFS_SMB_DIRECT
2018-04-17 22:17:08 +03:00
if ( server - > rdma ) {
if ( server - > sign )
wsize = min_t ( unsigned int ,
wsize , server - > smbd_conn - > max_fragmented_send_size ) ;
else
wsize = min_t ( unsigned int ,
2017-11-23 03:38:39 +03:00
wsize , server - > smbd_conn - > max_readwrite_size ) ;
2018-04-17 22:17:08 +03:00
}
2017-11-23 03:38:39 +03:00
# endif
2014-06-05 19:03:27 +04:00
if ( ! ( server - > capabilities & SMB2_GLOBAL_CAP_LARGE_MTU ) )
wsize = min_t ( unsigned int , wsize , SMB2_MAX_BUFFER_SIZE ) ;
2012-09-19 03:20:28 +04:00
return wsize ;
}
2018-09-25 23:33:47 +03:00
static unsigned int
smb3_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 : SMB3_DEFAULT_IOSIZE ;
wsize = min_t ( unsigned int , wsize , server - > max_write ) ;
# ifdef CONFIG_CIFS_SMB_DIRECT
if ( server - > rdma ) {
if ( server - > sign )
wsize = min_t ( unsigned int ,
wsize , server - > smbd_conn - > max_fragmented_send_size ) ;
else
wsize = min_t ( unsigned int ,
wsize , server - > smbd_conn - > max_readwrite_size ) ;
}
# endif
if ( ! ( server - > capabilities & SMB2_GLOBAL_CAP_LARGE_MTU ) )
wsize = min_t ( unsigned int , wsize , SMB2_MAX_BUFFER_SIZE ) ;
return wsize ;
}
2012-09-19 03:20:28 +04:00
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 ) ;
2017-11-23 03:38:39 +03:00
# ifdef CONFIG_CIFS_SMB_DIRECT
2018-04-17 22:17:08 +03:00
if ( server - > rdma ) {
if ( server - > sign )
rsize = min_t ( unsigned int ,
rsize , server - > smbd_conn - > max_fragmented_recv_size ) ;
else
rsize = min_t ( unsigned int ,
2017-11-23 03:38:39 +03:00
rsize , server - > smbd_conn - > max_readwrite_size ) ;
2018-04-17 22:17:08 +03:00
}
2017-11-23 03:38:39 +03:00
# endif
2014-06-25 11:28:57 +04:00
if ( ! ( server - > capabilities & SMB2_GLOBAL_CAP_LARGE_MTU ) )
rsize = min_t ( unsigned int , rsize , SMB2_MAX_BUFFER_SIZE ) ;
2012-09-19 03:20:28 +04:00
return rsize ;
}
2018-09-25 23:33:47 +03:00
static unsigned int
smb3_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 : SMB3_DEFAULT_IOSIZE ;
rsize = min_t ( unsigned int , rsize , server - > max_read ) ;
# ifdef CONFIG_CIFS_SMB_DIRECT
if ( server - > rdma ) {
if ( server - > sign )
rsize = min_t ( unsigned int ,
rsize , server - > smbd_conn - > max_fragmented_recv_size ) ;
else
rsize = min_t ( unsigned int ,
rsize , server - > smbd_conn - > max_readwrite_size ) ;
}
# endif
if ( ! ( server - > capabilities & SMB2_GLOBAL_CAP_LARGE_MTU ) )
rsize = min_t ( unsigned int , rsize , SMB2_MAX_BUFFER_SIZE ) ;
return rsize ;
}
2018-06-14 18:04:51 +03:00
static int
parse_server_interfaces ( struct network_interface_info_ioctl_rsp * buf ,
size_t buf_len ,
struct cifs_server_iface * * iface_list ,
size_t * iface_count )
{
struct network_interface_info_ioctl_rsp * p ;
struct sockaddr_in * addr4 ;
struct sockaddr_in6 * addr6 ;
struct iface_info_ipv4 * p4 ;
struct iface_info_ipv6 * p6 ;
struct cifs_server_iface * info ;
ssize_t bytes_left ;
size_t next = 0 ;
int nb_iface = 0 ;
int rc = 0 ;
* iface_list = NULL ;
* iface_count = 0 ;
/*
* Fist pass : count and sanity check
*/
bytes_left = buf_len ;
p = buf ;
while ( bytes_left > = sizeof ( * p ) ) {
nb_iface + + ;
next = le32_to_cpu ( p - > Next ) ;
if ( ! next ) {
bytes_left - = sizeof ( * p ) ;
break ;
}
p = ( struct network_interface_info_ioctl_rsp * ) ( ( u8 * ) p + next ) ;
bytes_left - = next ;
}
if ( ! nb_iface ) {
cifs_dbg ( VFS , " %s: malformed interface info \n " , __func__ ) ;
rc = - EINVAL ;
goto out ;
}
if ( bytes_left | | p - > Next )
cifs_dbg ( VFS , " %s: incomplete interface info \n " , __func__ ) ;
/*
* Second pass : extract info to internal structure
*/
* iface_list = kcalloc ( nb_iface , sizeof ( * * iface_list ) , GFP_KERNEL ) ;
if ( ! * iface_list ) {
rc = - ENOMEM ;
goto out ;
}
info = * iface_list ;
bytes_left = buf_len ;
p = buf ;
while ( bytes_left > = sizeof ( * p ) ) {
info - > speed = le64_to_cpu ( p - > LinkSpeed ) ;
info - > rdma_capable = le32_to_cpu ( p - > Capability & RDMA_CAPABLE ) ;
info - > rss_capable = le32_to_cpu ( p - > Capability & RSS_CAPABLE ) ;
cifs_dbg ( FYI , " %s: adding iface %zu \n " , __func__ , * iface_count ) ;
cifs_dbg ( FYI , " %s: speed %zu bps \n " , __func__ , info - > speed ) ;
cifs_dbg ( FYI , " %s: capabilities 0x%08x \n " , __func__ ,
le32_to_cpu ( p - > Capability ) ) ;
switch ( p - > Family ) {
/*
* The kernel and wire socket structures have the same
* layout and use network byte order but make the
* conversion explicit in case either one changes .
*/
case INTERNETWORK :
addr4 = ( struct sockaddr_in * ) & info - > sockaddr ;
p4 = ( struct iface_info_ipv4 * ) p - > Buffer ;
addr4 - > sin_family = AF_INET ;
memcpy ( & addr4 - > sin_addr , & p4 - > IPv4Address , 4 ) ;
/* [MS-SMB2] 2.2.32.5.1.1 Clients MUST ignore these */
addr4 - > sin_port = cpu_to_be16 ( CIFS_PORT ) ;
cifs_dbg ( FYI , " %s: ipv4 %pI4 \n " , __func__ ,
& addr4 - > sin_addr ) ;
break ;
case INTERNETWORKV6 :
addr6 = ( struct sockaddr_in6 * ) & info - > sockaddr ;
p6 = ( struct iface_info_ipv6 * ) p - > Buffer ;
addr6 - > sin6_family = AF_INET6 ;
memcpy ( & addr6 - > sin6_addr , & p6 - > IPv6Address , 16 ) ;
/* [MS-SMB2] 2.2.32.5.1.2 Clients MUST ignore these */
addr6 - > sin6_flowinfo = 0 ;
addr6 - > sin6_scope_id = 0 ;
addr6 - > sin6_port = cpu_to_be16 ( CIFS_PORT ) ;
cifs_dbg ( FYI , " %s: ipv6 %pI6 \n " , __func__ ,
& addr6 - > sin6_addr ) ;
break ;
default :
cifs_dbg ( VFS ,
" %s: skipping unsupported socket family \n " ,
__func__ ) ;
goto next_iface ;
}
( * iface_count ) + + ;
info + + ;
next_iface :
next = le32_to_cpu ( p - > Next ) ;
if ( ! next )
break ;
p = ( struct network_interface_info_ioctl_rsp * ) ( ( u8 * ) p + next ) ;
bytes_left - = next ;
}
if ( ! * iface_count ) {
rc = - EINVAL ;
goto out ;
}
out :
if ( rc ) {
kfree ( * iface_list ) ;
* iface_count = 0 ;
* iface_list = NULL ;
}
return rc ;
}
2013-10-14 10:21:53 +04:00
static int
SMB3_request_interfaces ( const unsigned int xid , struct cifs_tcon * tcon )
{
int rc ;
unsigned int ret_data_len = 0 ;
2018-06-14 18:04:51 +03:00
struct network_interface_info_ioctl_rsp * out_buf = NULL ;
struct cifs_server_iface * iface_list ;
size_t iface_count ;
struct cifs_ses * ses = tcon - > ses ;
2013-10-14 10:21:53 +04:00
rc = SMB2_ioctl ( xid , tcon , NO_FILE_ID , NO_FILE_ID ,
FSCTL_QUERY_NETWORK_INTERFACE_INFO , true /* is_fsctl */ ,
NULL /* no data input */ , 0 /* no data input */ ,
2019-03-29 06:32:49 +03:00
CIFSMaxBufSize , ( char * * ) & out_buf , & ret_data_len ) ;
2018-06-29 06:53:39 +03:00
if ( rc = = - EOPNOTSUPP ) {
cifs_dbg ( FYI ,
" server does not support query network interfaces \n " ) ;
goto out ;
} else if ( rc ! = 0 ) {
2014-10-17 00:13:14 +04:00
cifs_dbg ( VFS , " error %d on ioctl to get interface list \n " , rc ) ;
2018-06-14 18:04:51 +03:00
goto out ;
2014-10-17 00:13:14 +04:00
}
2018-06-14 18:04:51 +03:00
rc = parse_server_interfaces ( out_buf , ret_data_len ,
& iface_list , & iface_count ) ;
if ( rc )
goto out ;
spin_lock ( & ses - > iface_lock ) ;
kfree ( ses - > iface_list ) ;
ses - > iface_list = iface_list ;
ses - > iface_count = iface_count ;
ses - > iface_last_update = jiffies ;
spin_unlock ( & ses - > iface_lock ) ;
out :
2016-09-29 12:20:23 +03:00
kfree ( out_buf ) ;
2013-10-14 10:21:53 +04:00
return rc ;
}
2018-07-31 01:48:22 +03:00
static void
smb2_close_cached_fid ( struct kref * ref )
2018-06-13 23:48:35 +03:00
{
2018-07-31 01:48:22 +03:00
struct cached_fid * cfid = container_of ( ref , struct cached_fid ,
refcount ) ;
2018-06-13 23:48:35 +03:00
if ( cfid - > is_valid ) {
cifs_dbg ( FYI , " clear cached root file handle \n " ) ;
SMB2_close ( 0 , cfid - > tcon , cfid - > fid - > persistent_fid ,
cfid - > fid - > volatile_fid ) ;
cfid - > is_valid = false ;
2019-03-12 06:58:31 +03:00
cfid - > file_all_info_is_valid = false ;
2018-06-13 23:48:35 +03:00
}
2018-07-31 01:48:22 +03:00
}
void close_shroot ( struct cached_fid * cfid )
{
mutex_lock ( & cfid - > fid_mutex ) ;
kref_put ( & cfid - > refcount , smb2_close_cached_fid ) ;
2018-06-13 23:48:35 +03:00
mutex_unlock ( & cfid - > fid_mutex ) ;
}
2018-07-31 01:48:22 +03:00
void
smb2_cached_lease_break ( struct work_struct * work )
{
struct cached_fid * cfid = container_of ( work ,
struct cached_fid , lease_break ) ;
close_shroot ( cfid ) ;
}
2018-04-26 06:19:09 +03:00
/*
* Open the directory at the root of a share
*/
int open_shroot ( unsigned int xid , struct cifs_tcon * tcon , struct cifs_fid * pfid )
{
2019-03-12 06:58:31 +03:00
struct cifs_ses * ses = tcon - > ses ;
struct TCP_Server_Info * server = ses - > server ;
struct cifs_open_parms oparms ;
struct smb2_create_rsp * o_rsp = NULL ;
struct smb2_query_info_rsp * qi_rsp = NULL ;
int resp_buftype [ 2 ] ;
struct smb_rqst rqst [ 2 ] ;
struct kvec rsp_iov [ 2 ] ;
struct kvec open_iov [ SMB2_CREATE_IOV_SIZE ] ;
struct kvec qi_iov [ 1 ] ;
int rc , flags = 0 ;
__le16 utf16_path = 0 ; /* Null - since an open of top of share */
2018-06-13 23:48:35 +03:00
u8 oplock = SMB2_OPLOCK_LEVEL_II ;
2018-04-26 06:19:09 +03:00
2018-06-13 23:48:35 +03:00
mutex_lock ( & tcon - > crfid . fid_mutex ) ;
if ( tcon - > crfid . is_valid ) {
2018-04-26 06:19:09 +03:00
cifs_dbg ( FYI , " found a cached root file handle \n " ) ;
2018-06-13 23:48:35 +03:00
memcpy ( pfid , tcon - > crfid . fid , sizeof ( struct cifs_fid ) ) ;
2018-07-31 01:48:22 +03:00
kref_get ( & tcon - > crfid . refcount ) ;
2018-06-13 23:48:35 +03:00
mutex_unlock ( & tcon - > crfid . fid_mutex ) ;
2018-04-26 06:19:09 +03:00
return 0 ;
}
2019-03-12 06:58:31 +03:00
if ( smb3_encryption_required ( tcon ) )
flags | = CIFS_TRANSFORM_REQ ;
memset ( rqst , 0 , sizeof ( rqst ) ) ;
resp_buftype [ 0 ] = resp_buftype [ 1 ] = CIFS_NO_BUFFER ;
memset ( rsp_iov , 0 , sizeof ( rsp_iov ) ) ;
/* Open */
memset ( & open_iov , 0 , sizeof ( open_iov ) ) ;
rqst [ 0 ] . rq_iov = open_iov ;
rqst [ 0 ] . rq_nvec = SMB2_CREATE_IOV_SIZE ;
oparms . tcon = tcon ;
oparms . create_options = 0 ;
oparms . desired_access = FILE_READ_ATTRIBUTES ;
oparms . disposition = FILE_OPEN ;
oparms . fid = pfid ;
oparms . reconnect = false ;
rc = SMB2_open_init ( tcon , & rqst [ 0 ] , & oplock , & oparms , & utf16_path ) ;
if ( rc )
goto oshr_exit ;
smb2_set_next_command ( tcon , & rqst [ 0 ] ) ;
memset ( & qi_iov , 0 , sizeof ( qi_iov ) ) ;
rqst [ 1 ] . rq_iov = qi_iov ;
rqst [ 1 ] . rq_nvec = 1 ;
rc = SMB2_query_info_init ( tcon , & rqst [ 1 ] , COMPOUND_FID ,
COMPOUND_FID , FILE_ALL_INFORMATION ,
SMB2_O_INFO_FILE , 0 ,
sizeof ( struct smb2_file_all_info ) +
PATH_MAX * 2 , 0 , NULL ) ;
if ( rc )
goto oshr_exit ;
smb2_set_related ( & rqst [ 1 ] ) ;
2019-07-17 13:46:28 +03:00
/*
* We do not hold the lock for the open because in case
* SMB2_open needs to reconnect , it will end up calling
* cifs_mark_open_files_invalid ( ) which takes the lock again
* thus causing a deadlock
*/
mutex_unlock ( & tcon - > crfid . fid_mutex ) ;
2019-03-12 06:58:31 +03:00
rc = compound_send_recv ( xid , ses , flags , 2 , rqst ,
resp_buftype , rsp_iov ) ;
2019-07-17 13:46:28 +03:00
mutex_lock ( & tcon - > crfid . fid_mutex ) ;
/*
* Now we need to check again as the cached root might have
* been successfully re - opened from a concurrent process
*/
if ( tcon - > crfid . is_valid ) {
/* work was already done */
/* stash fids for close() later */
struct cifs_fid fid = {
. persistent_fid = pfid - > persistent_fid ,
. volatile_fid = pfid - > volatile_fid ,
} ;
/*
* caller expects this func to set pfid to a valid
* cached root , so we copy the existing one and get a
* reference .
*/
memcpy ( pfid , tcon - > crfid . fid , sizeof ( * pfid ) ) ;
kref_get ( & tcon - > crfid . refcount ) ;
mutex_unlock ( & tcon - > crfid . fid_mutex ) ;
if ( rc = = 0 ) {
/* close extra handle outside of crit sec */
SMB2_close ( xid , tcon , fid . persistent_fid , fid . volatile_fid ) ;
}
goto oshr_free ;
}
/* Cached root is still invalid, continue normaly */
2019-03-12 06:58:31 +03:00
if ( rc )
goto oshr_exit ;
o_rsp = ( struct smb2_create_rsp * ) rsp_iov [ 0 ] . iov_base ;
oparms . fid - > persistent_fid = o_rsp - > PersistentFileId ;
oparms . fid - > volatile_fid = o_rsp - > VolatileFileId ;
# ifdef CONFIG_CIFS_DEBUG2
oparms . fid - > mid = le64_to_cpu ( o_rsp - > sync_hdr . MessageId ) ;
# endif /* CIFS_DEBUG2 */
memcpy ( tcon - > crfid . fid , pfid , sizeof ( struct cifs_fid ) ) ;
tcon - > crfid . tcon = tcon ;
tcon - > crfid . is_valid = true ;
kref_init ( & tcon - > crfid . refcount ) ;
2019-07-19 01:22:18 +03:00
/* BB TBD check to see if oplock level check can be removed below */
2019-03-28 04:20:02 +03:00
if ( o_rsp - > OplockLevel = = SMB2_OPLOCK_LEVEL_LEASE ) {
kref_get ( & tcon - > crfid . refcount ) ;
2019-07-19 01:22:18 +03:00
smb2_parse_contexts ( server , o_rsp ,
& oparms . fid - > epoch ,
oparms . fid - > lease_key , & oplock , NULL ) ;
2019-03-28 04:20:02 +03:00
} else
goto oshr_exit ;
2019-03-12 06:58:31 +03:00
qi_rsp = ( struct smb2_query_info_rsp * ) rsp_iov [ 1 ] . iov_base ;
if ( le32_to_cpu ( qi_rsp - > OutputBufferLength ) < sizeof ( struct smb2_file_all_info ) )
goto oshr_exit ;
2019-04-01 02:53:44 +03:00
if ( ! smb2_validate_and_copy_iov (
2019-03-12 06:58:31 +03:00
le16_to_cpu ( qi_rsp - > OutputBufferOffset ) ,
sizeof ( struct smb2_file_all_info ) ,
& rsp_iov [ 1 ] , sizeof ( struct smb2_file_all_info ) ,
2019-04-01 02:53:44 +03:00
( char * ) & tcon - > crfid . file_all_info ) )
tcon - > crfid . file_all_info_is_valid = 1 ;
2019-03-12 06:58:31 +03:00
2019-07-17 13:46:28 +03:00
oshr_exit :
2018-06-13 23:48:35 +03:00
mutex_unlock ( & tcon - > crfid . fid_mutex ) ;
2019-07-17 13:46:28 +03:00
oshr_free :
2019-03-12 06:58:31 +03:00
SMB2_open_free ( & rqst [ 0 ] ) ;
SMB2_query_info_free ( & rqst [ 1 ] ) ;
free_rsp_buf ( resp_buftype [ 0 ] , rsp_iov [ 0 ] . iov_base ) ;
free_rsp_buf ( resp_buftype [ 1 ] , rsp_iov [ 1 ] . iov_base ) ;
2018-04-26 06:19:09 +03:00
return rc ;
}
2013-10-10 05:55:53 +04:00
static void
smb3_qfs_tcon ( const unsigned int xid , struct cifs_tcon * tcon )
{
int rc ;
__le16 srch_path = 0 ; /* Null - open root of share */
u8 oplock = SMB2_OPLOCK_LEVEL_NONE ;
struct cifs_open_parms oparms ;
struct cifs_fid fid ;
2018-04-26 06:19:09 +03:00
bool no_cached_open = tcon - > nohandlecache ;
2013-10-10 05:55:53 +04:00
oparms . tcon = tcon ;
oparms . desired_access = FILE_READ_ATTRIBUTES ;
oparms . disposition = FILE_OPEN ;
oparms . create_options = 0 ;
oparms . fid = & fid ;
oparms . reconnect = false ;
2018-04-26 06:19:09 +03:00
if ( no_cached_open )
2018-06-08 06:21:18 +03:00
rc = SMB2_open ( xid , & oparms , & srch_path , & oplock , NULL , NULL ,
NULL ) ;
2018-04-26 06:19:09 +03:00
else
rc = open_shroot ( xid , tcon , & fid ) ;
2013-10-10 05:55:53 +04:00
if ( rc )
return ;
2013-10-14 10:21:53 +04:00
SMB3_request_interfaces ( xid , tcon ) ;
2013-10-10 05:55:53 +04:00
SMB2_QFS_attr ( xid , tcon , fid . persistent_fid , fid . volatile_fid ,
FS_ATTRIBUTE_INFORMATION ) ;
SMB2_QFS_attr ( xid , tcon , fid . persistent_fid , fid . volatile_fid ,
FS_DEVICE_INFORMATION ) ;
2018-06-25 07:18:52 +03:00
SMB2_QFS_attr ( xid , tcon , fid . persistent_fid , fid . volatile_fid ,
FS_VOLUME_INFORMATION ) ;
2013-10-10 05:55:53 +04:00
SMB2_QFS_attr ( xid , tcon , fid . persistent_fid , fid . volatile_fid ,
FS_SECTOR_SIZE_INFORMATION ) ; /* SMB3 specific */
2018-04-26 06:19:09 +03:00
if ( no_cached_open )
SMB2_close ( xid , tcon , fid . persistent_fid , fid . volatile_fid ) ;
2018-07-31 01:48:22 +03:00
else
close_shroot ( & tcon - > crfid ) ;
2013-10-10 05:55:53 +04:00
}
2013-10-09 11:07:00 +04:00
static void
smb2_qfs_tcon ( const unsigned int xid , struct cifs_tcon * tcon )
{
int rc ;
__le16 srch_path = 0 ; /* Null - open root of share */
u8 oplock = SMB2_OPLOCK_LEVEL_NONE ;
struct cifs_open_parms oparms ;
struct cifs_fid fid ;
oparms . tcon = tcon ;
oparms . desired_access = FILE_READ_ATTRIBUTES ;
oparms . disposition = FILE_OPEN ;
oparms . create_options = 0 ;
oparms . fid = & fid ;
oparms . reconnect = false ;
2018-06-08 06:21:18 +03:00
rc = SMB2_open ( xid , & oparms , & srch_path , & oplock , NULL , NULL , NULL ) ;
2013-10-09 11:07:00 +04:00
if ( rc )
return ;
2013-10-09 22:36:35 +04:00
SMB2_QFS_attr ( xid , tcon , fid . persistent_fid , fid . volatile_fid ,
FS_ATTRIBUTE_INFORMATION ) ;
SMB2_QFS_attr ( xid , tcon , fid . persistent_fid , fid . volatile_fid ,
FS_DEVICE_INFORMATION ) ;
2013-10-09 11:07:00 +04:00
SMB2_close ( xid , tcon , fid . persistent_fid , fid . volatile_fid ) ;
}
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 ;
__le16 * utf16_path ;
2012-09-19 03:20:33 +04:00
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE ;
2013-07-09 18:20:30 +04:00
struct cifs_open_parms oparms ;
struct cifs_fid fid ;
2011-12-26 22:58:46 +04:00
2018-06-13 23:48:35 +03:00
if ( ( * full_path = = 0 ) & & tcon - > crfid . is_valid )
2018-04-26 06:19:09 +03:00
return 0 ;
2011-12-26 22:58:46 +04:00
utf16_path = cifs_convert_path_to_utf16 ( full_path , cifs_sb ) ;
if ( ! utf16_path )
return - ENOMEM ;
2013-07-09 18:20:30 +04:00
oparms . tcon = tcon ;
oparms . desired_access = FILE_READ_ATTRIBUTES ;
oparms . disposition = FILE_OPEN ;
2018-08-28 01:04:13 +03:00
if ( backup_cred ( cifs_sb ) )
oparms . create_options = CREATE_OPEN_BACKUP_INTENT ;
else
oparms . create_options = 0 ;
2013-07-09 18:20:30 +04:00
oparms . fid = & fid ;
2013-07-09 18:40:58 +04:00
oparms . reconnect = false ;
2013-07-09 18:20:30 +04:00
2018-06-08 06:21:18 +03:00
rc = SMB2_open ( xid , & oparms , utf16_path , & oplock , NULL , NULL , NULL ) ;
2011-12-26 22:58:46 +04:00
if ( rc ) {
kfree ( utf16_path ) ;
return rc ;
}
2013-07-09 18:20:30 +04:00
rc = SMB2_close ( xid , tcon , fid . persistent_fid , fid . volatile_fid ) ;
2011-12-26 22:58:46 +04:00
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 ;
2014-08-22 13:32:11 +04:00
smb2_data = kzalloc ( sizeof ( struct smb2_file_all_info ) + PATH_MAX * 2 ,
2012-09-19 03:20:27 +04:00
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 ;
}
2017-09-05 12:24:15 +03:00
# ifdef CONFIG_CIFS_XATTR
2017-08-24 04:24:55 +03:00
static ssize_t
move_smb2_ea_to_cifs ( char * dst , size_t dst_size ,
struct smb2_file_full_ea_info * src , size_t src_size ,
const unsigned char * ea_name )
{
int rc = 0 ;
unsigned int ea_name_len = ea_name ? strlen ( ea_name ) : 0 ;
char * name , * value ;
2018-10-25 08:43:36 +03:00
size_t buf_size = dst_size ;
2017-08-24 04:24:55 +03:00
size_t name_len , value_len , user_name_len ;
while ( src_size > 0 ) {
name = & src - > ea_data [ 0 ] ;
name_len = ( size_t ) src - > ea_name_length ;
value = & src - > ea_data [ src - > ea_name_length + 1 ] ;
value_len = ( size_t ) le16_to_cpu ( src - > ea_value_length ) ;
2019-05-08 22:36:25 +03:00
if ( name_len = = 0 )
2017-08-24 04:24:55 +03:00
break ;
if ( src_size < 8 + name_len + 1 + value_len ) {
cifs_dbg ( FYI , " EA entry goes beyond length of list \n " ) ;
rc = - EIO ;
goto out ;
}
if ( ea_name ) {
if ( ea_name_len = = name_len & &
memcmp ( ea_name , name , name_len ) = = 0 ) {
rc = value_len ;
if ( dst_size = = 0 )
goto out ;
if ( dst_size < value_len ) {
rc = - ERANGE ;
goto out ;
}
memcpy ( dst , value , value_len ) ;
goto out ;
}
} else {
/* 'user.' plus a terminating null */
user_name_len = 5 + 1 + name_len ;
2018-10-25 08:43:36 +03:00
if ( buf_size = = 0 ) {
/* skip copy - calc size only */
rc + = user_name_len ;
} else if ( dst_size > = user_name_len ) {
2017-08-24 04:24:55 +03:00
dst_size - = user_name_len ;
memcpy ( dst , " user. " , 5 ) ;
dst + = 5 ;
memcpy ( dst , src - > ea_data , name_len ) ;
dst + = name_len ;
* dst = 0 ;
+ + dst ;
2018-10-25 08:43:36 +03:00
rc + = user_name_len ;
2017-08-24 04:24:55 +03:00
} else {
/* stop before overrun buffer */
rc = - ERANGE ;
break ;
}
}
if ( ! src - > next_entry_offset )
break ;
if ( src_size < le32_to_cpu ( src - > next_entry_offset ) ) {
/* stop before overrun buffer */
rc = - ERANGE ;
break ;
}
src_size - = le32_to_cpu ( src - > next_entry_offset ) ;
src = ( void * ) ( ( char * ) src +
le32_to_cpu ( src - > next_entry_offset ) ) ;
}
/* didn't find the named attribute */
if ( ea_name )
rc = - ENODATA ;
out :
return ( ssize_t ) rc ;
}
static ssize_t
smb2_query_eas ( const unsigned int xid , struct cifs_tcon * tcon ,
const unsigned char * path , const unsigned char * ea_name ,
char * ea_data , size_t buf_size ,
struct cifs_sb_info * cifs_sb )
{
int rc ;
__le16 * utf16_path ;
2018-11-27 02:52:04 +03:00
struct kvec rsp_iov = { NULL , 0 } ;
int buftype = CIFS_NO_BUFFER ;
struct smb2_query_info_rsp * rsp ;
struct smb2_file_full_ea_info * info = NULL ;
2017-08-24 04:24:55 +03:00
utf16_path = cifs_convert_path_to_utf16 ( path , cifs_sb ) ;
if ( ! utf16_path )
return - ENOMEM ;
2018-11-27 02:52:04 +03:00
rc = smb2_query_info_compound ( xid , tcon , utf16_path ,
FILE_READ_EA ,
FILE_FULL_EA_INFORMATION ,
SMB2_O_INFO_FILE ,
2019-01-29 05:46:17 +03:00
CIFSMaxBufSize -
MAX_SMB2_CREATE_RESPONSE_SIZE -
MAX_SMB2_CLOSE_RESPONSE_SIZE ,
2018-11-27 02:52:04 +03:00
& rsp_iov , & buftype , cifs_sb ) ;
2017-08-24 04:24:55 +03:00
if ( rc ) {
2018-11-27 02:52:04 +03:00
/*
* If ea_name is NULL ( listxattr ) and there are no EAs ,
* return 0 as it ' s not an error . Otherwise , the specified
* ea_name was not found .
*/
if ( ! ea_name & & rc = = - ENODATA )
rc = 0 ;
goto qeas_exit ;
2017-08-24 04:24:55 +03:00
}
2018-11-27 02:52:04 +03:00
rsp = ( struct smb2_query_info_rsp * ) rsp_iov . iov_base ;
rc = smb2_validate_iov ( le16_to_cpu ( rsp - > OutputBufferOffset ) ,
le32_to_cpu ( rsp - > OutputBufferLength ) ,
& rsp_iov ,
sizeof ( struct smb2_file_full_ea_info ) ) ;
if ( rc )
goto qeas_exit ;
2017-08-24 04:24:55 +03:00
2018-11-27 02:52:04 +03:00
info = ( struct smb2_file_full_ea_info * ) (
le16_to_cpu ( rsp - > OutputBufferOffset ) + ( char * ) rsp ) ;
rc = move_smb2_ea_to_cifs ( ea_data , buf_size , info ,
le32_to_cpu ( rsp - > OutputBufferLength ) , ea_name ) ;
2017-08-24 04:24:55 +03:00
2018-11-27 02:52:04 +03:00
qeas_exit :
kfree ( utf16_path ) ;
free_rsp_buf ( buftype , rsp_iov . iov_base ) ;
2017-08-24 04:24:55 +03:00
return rc ;
}
2017-08-24 04:24:56 +03:00
static int
smb2_set_ea ( const unsigned int xid , struct cifs_tcon * tcon ,
const char * path , const char * ea_name , const void * ea_value ,
const __u16 ea_value_len , const struct nls_table * nls_codepage ,
struct cifs_sb_info * cifs_sb )
{
2018-11-06 15:52:43 +03:00
struct cifs_ses * ses = tcon - > ses ;
__le16 * utf16_path = NULL ;
2017-08-24 04:24:56 +03:00
int ea_name_len = strlen ( ea_name ) ;
2018-11-06 15:52:43 +03:00
int flags = 0 ;
2017-08-24 04:24:56 +03:00
int len ;
2018-11-06 15:52:43 +03:00
struct smb_rqst rqst [ 3 ] ;
int resp_buftype [ 3 ] ;
struct kvec rsp_iov [ 3 ] ;
struct kvec open_iov [ SMB2_CREATE_IOV_SIZE ] ;
struct cifs_open_parms oparms ;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE ;
struct cifs_fid fid ;
struct kvec si_iov [ SMB2_SET_INFO_IOV_SIZE ] ;
unsigned int size [ 1 ] ;
void * data [ 1 ] ;
struct smb2_file_full_ea_info * ea = NULL ;
struct kvec close_iov [ 1 ] ;
int rc ;
if ( smb3_encryption_required ( tcon ) )
flags | = CIFS_TRANSFORM_REQ ;
2017-08-24 04:24:56 +03:00
if ( ea_name_len > 255 )
return - EINVAL ;
utf16_path = cifs_convert_path_to_utf16 ( path , cifs_sb ) ;
if ( ! utf16_path )
return - ENOMEM ;
2018-11-06 15:52:43 +03:00
memset ( rqst , 0 , sizeof ( rqst ) ) ;
resp_buftype [ 0 ] = resp_buftype [ 1 ] = resp_buftype [ 2 ] = CIFS_NO_BUFFER ;
memset ( rsp_iov , 0 , sizeof ( rsp_iov ) ) ;
2019-02-07 08:48:44 +03:00
if ( ses - > server - > ops - > query_all_EAs ) {
if ( ! ea_value ) {
rc = ses - > server - > ops - > query_all_EAs ( xid , tcon , path ,
ea_name , NULL , 0 ,
cifs_sb ) ;
if ( rc = = - ENODATA )
goto sea_exit ;
}
}
2018-11-06 15:52:43 +03:00
/* Open */
memset ( & open_iov , 0 , sizeof ( open_iov ) ) ;
rqst [ 0 ] . rq_iov = open_iov ;
rqst [ 0 ] . rq_nvec = SMB2_CREATE_IOV_SIZE ;
memset ( & oparms , 0 , sizeof ( oparms ) ) ;
2017-08-24 04:24:56 +03:00
oparms . tcon = tcon ;
oparms . desired_access = FILE_WRITE_EA ;
oparms . disposition = FILE_OPEN ;
2018-08-28 01:04:13 +03:00
if ( backup_cred ( cifs_sb ) )
oparms . create_options = CREATE_OPEN_BACKUP_INTENT ;
else
oparms . create_options = 0 ;
2017-08-24 04:24:56 +03:00
oparms . fid = & fid ;
oparms . reconnect = false ;
2018-11-06 15:52:43 +03:00
rc = SMB2_open_init ( tcon , & rqst [ 0 ] , & oplock , & oparms , utf16_path ) ;
if ( rc )
goto sea_exit ;
2018-12-31 06:43:40 +03:00
smb2_set_next_command ( tcon , & rqst [ 0 ] ) ;
2018-11-06 15:52:43 +03:00
/* Set Info */
memset ( & si_iov , 0 , sizeof ( si_iov ) ) ;
rqst [ 1 ] . rq_iov = si_iov ;
rqst [ 1 ] . rq_nvec = 1 ;
2017-08-24 04:24:56 +03:00
len = sizeof ( ea ) + ea_name_len + ea_value_len + 1 ;
ea = kzalloc ( len , GFP_KERNEL ) ;
if ( ea = = NULL ) {
2018-11-06 15:52:43 +03:00
rc = - ENOMEM ;
goto sea_exit ;
2017-08-24 04:24:56 +03:00
}
ea - > ea_name_length = ea_name_len ;
ea - > ea_value_length = cpu_to_le16 ( ea_value_len ) ;
memcpy ( ea - > ea_data , ea_name , ea_name_len + 1 ) ;
memcpy ( ea - > ea_data + ea_name_len + 1 , ea_value , ea_value_len ) ;
2018-11-06 15:52:43 +03:00
size [ 0 ] = len ;
data [ 0 ] = ea ;
rc = SMB2_set_info_init ( tcon , & rqst [ 1 ] , COMPOUND_FID ,
COMPOUND_FID , current - > tgid ,
FILE_FULL_EA_INFORMATION ,
SMB2_O_INFO_FILE , 0 , data , size ) ;
2018-12-31 06:43:40 +03:00
smb2_set_next_command ( tcon , & rqst [ 1 ] ) ;
2018-11-06 15:52:43 +03:00
smb2_set_related ( & rqst [ 1 ] ) ;
2018-07-04 20:16:16 +03:00
2017-08-24 04:24:56 +03:00
2018-11-06 15:52:43 +03:00
/* Close */
memset ( & close_iov , 0 , sizeof ( close_iov ) ) ;
rqst [ 2 ] . rq_iov = close_iov ;
rqst [ 2 ] . rq_nvec = 1 ;
rc = SMB2_close_init ( tcon , & rqst [ 2 ] , COMPOUND_FID , COMPOUND_FID ) ;
smb2_set_related ( & rqst [ 2 ] ) ;
rc = compound_send_recv ( xid , ses , flags , 3 , rqst ,
resp_buftype , rsp_iov ) ;
sea_exit :
kfree ( ea ) ;
kfree ( utf16_path ) ;
SMB2_open_free ( & rqst [ 0 ] ) ;
SMB2_set_info_free ( & rqst [ 1 ] ) ;
SMB2_close_free ( & rqst [ 2 ] ) ;
free_rsp_buf ( resp_buftype [ 0 ] , rsp_iov [ 0 ] . iov_base ) ;
free_rsp_buf ( resp_buftype [ 1 ] , rsp_iov [ 1 ] . iov_base ) ;
free_rsp_buf ( resp_buftype [ 2 ] , rsp_iov [ 2 ] . iov_base ) ;
2017-08-24 04:24:56 +03:00
return rc ;
}
2017-09-05 12:24:15 +03:00
# endif
2017-08-24 04:24:56 +03:00
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 )
{
int i ;
2019-05-08 22:36:25 +03:00
2012-05-28 15:19:39 +04:00
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 ) ;
}
}
2013-06-19 23:15:30 +04:00
static void
smb2_dump_share_caps ( struct seq_file * m , struct cifs_tcon * tcon )
{
seq_puts ( m , " \n \t Share Capabilities: " ) ;
if ( tcon - > capabilities & SMB2_SHARE_CAP_DFS )
seq_puts ( m , " DFS, " ) ;
if ( tcon - > capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY )
seq_puts ( m , " CONTINUOUS AVAILABILITY, " ) ;
if ( tcon - > capabilities & SMB2_SHARE_CAP_SCALEOUT )
seq_puts ( m , " SCALEOUT, " ) ;
if ( tcon - > capabilities & SMB2_SHARE_CAP_CLUSTER )
seq_puts ( m , " CLUSTER, " ) ;
if ( tcon - > capabilities & SMB2_SHARE_CAP_ASYMMETRIC )
seq_puts ( m , " ASYMMETRIC, " ) ;
if ( tcon - > capabilities = = 0 )
seq_puts ( m , " None " ) ;
2013-10-10 05:55:53 +04:00
if ( tcon - > ss_flags & SSINFO_FLAGS_ALIGNED_DEVICE )
seq_puts ( m , " Aligned, " ) ;
if ( tcon - > ss_flags & SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE )
seq_puts ( m , " Partition Aligned, " ) ;
if ( tcon - > ss_flags & SSINFO_FLAGS_NO_SEEK_PENALTY )
seq_puts ( m , " SSD, " ) ;
if ( tcon - > ss_flags & SSINFO_FLAGS_TRIM_ENABLED )
seq_puts ( m , " TRIM-support, " ) ;
2013-06-19 23:15:30 +04:00
seq_printf ( m , " \t Share Flags: 0x%x " , tcon - > share_flags ) ;
2018-05-20 09:27:03 +03:00
seq_printf ( m , " \n \t tid: 0x%x " , tcon - > tid ) ;
2013-10-10 05:55:53 +04:00
if ( tcon - > perf_sector_size )
seq_printf ( m , " \t Optimal sector size: 0x%x " ,
tcon - > perf_sector_size ) ;
2018-05-20 09:27:03 +03:00
seq_printf ( m , " \t Maximal Access: 0x%x " , tcon - > maximal_access ) ;
2013-06-19 23:15:30 +04:00
}
2012-05-28 15:19:39 +04:00
static void
smb2_print_stats ( struct seq_file * m , struct cifs_tcon * tcon )
{
atomic_t * sent = tcon - > stats . smb2_stats . smb2_com_sent ;
atomic_t * failed = tcon - > stats . smb2_stats . smb2_com_failed ;
2018-07-27 23:14:04 +03:00
/*
* Can ' t display SMB2_NEGOTIATE , SESSION_SETUP , LOGOFF , CANCEL and ECHO
* totals ( requests sent ) since those SMBs are per - session not per tcon
*/
2018-07-31 09:46:47 +03:00
seq_printf ( m , " \n Bytes read: %llu Bytes written: %llu " ,
( long long ) ( tcon - > bytes_read ) ,
( long long ) ( tcon - > bytes_written ) ) ;
2018-10-20 01:14:32 +03:00
seq_printf ( m , " \n Open files: %d total (local), %d open on server " ,
atomic_read ( & tcon - > num_local_opens ) ,
atomic_read ( & tcon - > num_remote_opens ) ) ;
2018-07-27 23:14:04 +03:00
seq_printf ( m , " \n TreeConnects: %d total %d failed " ,
2012-05-28 15:19:39 +04:00
atomic_read ( & sent [ SMB2_TREE_CONNECT_HE ] ) ,
atomic_read ( & failed [ SMB2_TREE_CONNECT_HE ] ) ) ;
2018-07-27 23:14:04 +03:00
seq_printf ( m , " \n TreeDisconnects: %d total %d failed " ,
2012-05-28 15:19:39 +04:00
atomic_read ( & sent [ SMB2_TREE_DISCONNECT_HE ] ) ,
atomic_read ( & failed [ SMB2_TREE_DISCONNECT_HE ] ) ) ;
2018-07-27 23:14:04 +03:00
seq_printf ( m , " \n Creates: %d total %d failed " ,
2012-05-28 15:19:39 +04:00
atomic_read ( & sent [ SMB2_CREATE_HE ] ) ,
atomic_read ( & failed [ SMB2_CREATE_HE ] ) ) ;
2018-07-27 23:14:04 +03:00
seq_printf ( m , " \n Closes: %d total %d failed " ,
2012-05-28 15:19:39 +04:00
atomic_read ( & sent [ SMB2_CLOSE_HE ] ) ,
atomic_read ( & failed [ SMB2_CLOSE_HE ] ) ) ;
2018-07-27 23:14:04 +03:00
seq_printf ( m , " \n Flushes: %d total %d failed " ,
2012-05-28 15:19:39 +04:00
atomic_read ( & sent [ SMB2_FLUSH_HE ] ) ,
atomic_read ( & failed [ SMB2_FLUSH_HE ] ) ) ;
2018-07-27 23:14:04 +03:00
seq_printf ( m , " \n Reads: %d total %d failed " ,
2012-05-28 15:19:39 +04:00
atomic_read ( & sent [ SMB2_READ_HE ] ) ,
atomic_read ( & failed [ SMB2_READ_HE ] ) ) ;
2018-07-27 23:14:04 +03:00
seq_printf ( m , " \n Writes: %d total %d failed " ,
2012-05-28 15:19:39 +04:00
atomic_read ( & sent [ SMB2_WRITE_HE ] ) ,
atomic_read ( & failed [ SMB2_WRITE_HE ] ) ) ;
2018-07-27 23:14:04 +03:00
seq_printf ( m , " \n Locks: %d total %d failed " ,
2012-05-28 15:19:39 +04:00
atomic_read ( & sent [ SMB2_LOCK_HE ] ) ,
atomic_read ( & failed [ SMB2_LOCK_HE ] ) ) ;
2018-07-27 23:14:04 +03:00
seq_printf ( m , " \n IOCTLs: %d total %d failed " ,
2012-05-28 15:19:39 +04:00
atomic_read ( & sent [ SMB2_IOCTL_HE ] ) ,
atomic_read ( & failed [ SMB2_IOCTL_HE ] ) ) ;
2018-07-27 23:14:04 +03:00
seq_printf ( m , " \n QueryDirectories: %d total %d failed " ,
2012-05-28 15:19:39 +04:00
atomic_read ( & sent [ SMB2_QUERY_DIRECTORY_HE ] ) ,
atomic_read ( & failed [ SMB2_QUERY_DIRECTORY_HE ] ) ) ;
2018-07-27 23:14:04 +03:00
seq_printf ( m , " \n ChangeNotifies: %d total %d failed " ,
2012-05-28 15:19:39 +04:00
atomic_read ( & sent [ SMB2_CHANGE_NOTIFY_HE ] ) ,
atomic_read ( & failed [ SMB2_CHANGE_NOTIFY_HE ] ) ) ;
2018-07-27 23:14:04 +03:00
seq_printf ( m , " \n QueryInfos: %d total %d failed " ,
2012-05-28 15:19:39 +04:00
atomic_read ( & sent [ SMB2_QUERY_INFO_HE ] ) ,
atomic_read ( & failed [ SMB2_QUERY_INFO_HE ] ) ) ;
2018-07-27 23:14:04 +03:00
seq_printf ( m , " \n SetInfos: %d total %d failed " ,
2012-05-28 15:19:39 +04:00
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 ] ) ) ;
}
2012-09-19 03:20:26 +04:00
static void
smb2_set_fid ( struct cifsFileInfo * cfile , struct cifs_fid * fid , __u32 oplock )
{
2015-03-18 01:25:59 +03:00
struct cifsInodeInfo * cinode = CIFS_I ( d_inode ( cfile - > dentry ) ) ;
2013-09-05 16:11:28 +04:00
struct TCP_Server_Info * server = tlink_tcon ( cfile - > tlink ) - > ses - > server ;
2012-09-19 03:20:26 +04:00
cfile - > fid . persistent_fid = fid - > persistent_fid ;
cfile - > fid . volatile_fid = fid - > volatile_fid ;
2018-10-31 03:50:31 +03:00
# ifdef CONFIG_CIFS_DEBUG2
cfile - > fid . mid = fid - > mid ;
# endif /* CIFS_DEBUG2 */
2013-09-05 21:30:16 +04:00
server - > ops - > set_oplock_level ( cinode , oplock , fid - > epoch ,
& fid - > purge_cache ) ;
2013-09-05 13:01:06 +04:00
cinode - > can_cache_brlcks = CIFS_CACHE_WRITE ( cinode ) ;
2016-09-22 08:38:50 +03:00
memcpy ( cfile - > fid . create_guid , fid - > create_guid , 16 ) ;
2012-09-19 03:20:26 +04:00
}
2012-09-25 11:00:07 +04:00
static void
2012-09-19 03:20:26 +04:00
smb2_close_file ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifs_fid * fid )
{
2012-09-25 11:00:07 +04:00
SMB2_close ( xid , tcon , fid - > persistent_fid , fid - > volatile_fid ) ;
2012-09-19 03:20:26 +04:00
}
2013-11-14 10:05:36 +04:00
static int
SMB2_request_res_key ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid ,
struct copychunk_ioctl * pcchunk )
{
int rc ;
unsigned int ret_data_len ;
struct resume_key_req * res_key ;
rc = SMB2_ioctl ( xid , tcon , persistent_fid , volatile_fid ,
FSCTL_SRV_REQUEST_RESUME_KEY , true /* is_fsctl */ ,
2019-03-29 06:32:49 +03:00
NULL , 0 /* no input */ , CIFSMaxBufSize ,
2013-11-14 10:05:36 +04:00
( char * * ) & res_key , & ret_data_len ) ;
if ( rc ) {
cifs_dbg ( VFS , " refcpy ioctl error %d getting resume key \n " , rc ) ;
goto req_res_key_exit ;
}
if ( ret_data_len < sizeof ( struct resume_key_req ) ) {
cifs_dbg ( VFS , " Invalid refcopy resume key length \n " ) ;
rc = - EINVAL ;
goto req_res_key_exit ;
}
memcpy ( pcchunk - > SourceKey , res_key - > ResumeKey , COPY_CHUNK_RES_KEY_SIZE ) ;
req_res_key_exit :
kfree ( res_key ) ;
return rc ;
}
2018-10-08 03:19:58 +03:00
static int
smb2_ioctl_query_info ( const unsigned int xid ,
2018-10-16 22:47:58 +03:00
struct cifs_tcon * tcon ,
__le16 * path , int is_dir ,
2018-10-08 03:19:58 +03:00
unsigned long p )
{
struct cifs_ses * ses = tcon - > ses ;
char __user * arg = ( char __user * ) p ;
struct smb_query_info qi ;
struct smb_query_info __user * pqi ;
int rc = 0 ;
int flags = 0 ;
2019-03-15 02:07:22 +03:00
struct smb2_query_info_rsp * qi_rsp = NULL ;
struct smb2_ioctl_rsp * io_rsp = NULL ;
2018-10-16 22:47:58 +03:00
void * buffer = NULL ;
struct smb_rqst rqst [ 3 ] ;
int resp_buftype [ 3 ] ;
struct kvec rsp_iov [ 3 ] ;
struct kvec open_iov [ SMB2_CREATE_IOV_SIZE ] ;
struct cifs_open_parms oparms ;
u8 oplock = SMB2_OPLOCK_LEVEL_NONE ;
struct cifs_fid fid ;
struct kvec qi_iov [ 1 ] ;
2019-03-15 02:07:22 +03:00
struct kvec io_iov [ SMB2_IOCTL_IOV_SIZE ] ;
2018-10-16 22:47:58 +03:00
struct kvec close_iov [ 1 ] ;
memset ( rqst , 0 , sizeof ( rqst ) ) ;
resp_buftype [ 0 ] = resp_buftype [ 1 ] = resp_buftype [ 2 ] = CIFS_NO_BUFFER ;
memset ( rsp_iov , 0 , sizeof ( rsp_iov ) ) ;
2018-10-08 03:19:58 +03:00
if ( copy_from_user ( & qi , arg , sizeof ( struct smb_query_info ) ) )
return - EFAULT ;
if ( qi . output_buffer_length > 1024 )
return - EINVAL ;
if ( ! ses | | ! ( ses - > server ) )
return - EIO ;
if ( smb3_encryption_required ( tcon ) )
flags | = CIFS_TRANSFORM_REQ ;
buffer = kmalloc ( qi . output_buffer_length , GFP_KERNEL ) ;
if ( buffer = = NULL )
return - ENOMEM ;
if ( copy_from_user ( buffer , arg + sizeof ( struct smb_query_info ) ,
qi . output_buffer_length ) ) {
2018-10-16 22:47:58 +03:00
rc = - EFAULT ;
goto iqinf_exit ;
2018-10-08 03:19:58 +03:00
}
2018-10-16 22:47:58 +03:00
/* Open */
memset ( & open_iov , 0 , sizeof ( open_iov ) ) ;
rqst [ 0 ] . rq_iov = open_iov ;
rqst [ 0 ] . rq_nvec = SMB2_CREATE_IOV_SIZE ;
memset ( & oparms , 0 , sizeof ( oparms ) ) ;
oparms . tcon = tcon ;
oparms . desired_access = FILE_READ_ATTRIBUTES | READ_CONTROL ;
oparms . disposition = FILE_OPEN ;
if ( is_dir )
oparms . create_options = CREATE_NOT_FILE ;
else
oparms . create_options = CREATE_NOT_DIR ;
oparms . fid = & fid ;
oparms . reconnect = false ;
2019-04-11 05:20:17 +03:00
/*
* FSCTL codes encode the special access they need in the fsctl code .
*/
if ( qi . flags & PASSTHRU_FSCTL ) {
switch ( qi . info_type & FSCTL_DEVICE_ACCESS_MASK ) {
case FSCTL_DEVICE_ACCESS_FILE_READ_WRITE_ACCESS :
oparms . desired_access = FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | SYNCHRONIZE ;
2019-04-11 21:53:17 +03:00
break ;
case FSCTL_DEVICE_ACCESS_FILE_ANY_ACCESS :
oparms . desired_access = GENERIC_ALL ;
break ;
case FSCTL_DEVICE_ACCESS_FILE_READ_ACCESS :
oparms . desired_access = GENERIC_READ ;
break ;
case FSCTL_DEVICE_ACCESS_FILE_WRITE_ACCESS :
oparms . desired_access = GENERIC_WRITE ;
2019-04-11 05:20:17 +03:00
break ;
}
}
2018-10-16 22:47:58 +03:00
rc = SMB2_open_init ( tcon , & rqst [ 0 ] , & oplock , & oparms , path ) ;
if ( rc )
goto iqinf_exit ;
2018-12-31 06:43:40 +03:00
smb2_set_next_command ( tcon , & rqst [ 0 ] ) ;
2018-10-16 22:47:58 +03:00
/* Query */
2019-03-13 10:40:07 +03:00
if ( qi . flags & PASSTHRU_FSCTL ) {
/* Can eventually relax perm check since server enforces too */
if ( ! capable ( CAP_SYS_ADMIN ) )
rc = - EPERM ;
2019-03-15 02:07:22 +03:00
else {
memset ( & io_iov , 0 , sizeof ( io_iov ) ) ;
rqst [ 1 ] . rq_iov = io_iov ;
rqst [ 1 ] . rq_nvec = SMB2_IOCTL_IOV_SIZE ;
rc = SMB2_ioctl_init ( tcon , & rqst [ 1 ] ,
COMPOUND_FID , COMPOUND_FID ,
2019-04-11 05:20:17 +03:00
qi . info_type , true , buffer ,
qi . output_buffer_length ,
CIFSMaxBufSize ) ;
2019-03-15 02:07:22 +03:00
}
2019-03-13 10:40:07 +03:00
} else if ( qi . flags = = PASSTHRU_QUERY_INFO ) {
memset ( & qi_iov , 0 , sizeof ( qi_iov ) ) ;
rqst [ 1 ] . rq_iov = qi_iov ;
rqst [ 1 ] . rq_nvec = 1 ;
rc = SMB2_query_info_init ( tcon , & rqst [ 1 ] , COMPOUND_FID ,
COMPOUND_FID , qi . file_info_class ,
qi . info_type , qi . additional_information ,
2018-10-08 03:19:58 +03:00
qi . input_buffer_length ,
qi . output_buffer_length , buffer ) ;
2019-03-13 10:40:07 +03:00
} else { /* unknown flags */
cifs_dbg ( VFS , " invalid passthru query flags: 0x%x \n " , qi . flags ) ;
rc = - EINVAL ;
}
2018-10-08 03:19:58 +03:00
if ( rc )
goto iqinf_exit ;
2018-12-31 06:43:40 +03:00
smb2_set_next_command ( tcon , & rqst [ 1 ] ) ;
2018-10-16 22:47:58 +03:00
smb2_set_related ( & rqst [ 1 ] ) ;
/* Close */
memset ( & close_iov , 0 , sizeof ( close_iov ) ) ;
rqst [ 2 ] . rq_iov = close_iov ;
rqst [ 2 ] . rq_nvec = 1 ;
2018-10-08 03:19:58 +03:00
2018-10-16 22:47:58 +03:00
rc = SMB2_close_init ( tcon , & rqst [ 2 ] , COMPOUND_FID , COMPOUND_FID ) ;
2018-10-08 03:19:58 +03:00
if ( rc )
goto iqinf_exit ;
2018-10-16 22:47:58 +03:00
smb2_set_related ( & rqst [ 2 ] ) ;
2018-10-08 03:19:58 +03:00
2018-10-16 22:47:58 +03:00
rc = compound_send_recv ( xid , ses , flags , 3 , rqst ,
resp_buftype , rsp_iov ) ;
if ( rc )
goto iqinf_exit ;
2019-03-15 02:07:22 +03:00
if ( qi . flags & PASSTHRU_FSCTL ) {
pqi = ( struct smb_query_info __user * ) arg ;
io_rsp = ( struct smb2_ioctl_rsp * ) rsp_iov [ 1 ] . iov_base ;
if ( le32_to_cpu ( io_rsp - > OutputCount ) < qi . input_buffer_length )
qi . input_buffer_length = le32_to_cpu ( io_rsp - > OutputCount ) ;
2019-04-15 05:13:52 +03:00
if ( qi . input_buffer_length > 0 & &
le32_to_cpu ( io_rsp - > OutputOffset ) + qi . input_buffer_length > rsp_iov [ 1 ] . iov_len ) {
rc = - EFAULT ;
goto iqinf_exit ;
}
2019-03-15 02:07:22 +03:00
if ( copy_to_user ( & pqi - > input_buffer_length , & qi . input_buffer_length ,
sizeof ( qi . input_buffer_length ) ) ) {
rc = - EFAULT ;
goto iqinf_exit ;
}
2019-04-15 05:13:52 +03:00
if ( copy_to_user ( ( void __user * ) pqi + sizeof ( struct smb_query_info ) ,
( const void * ) io_rsp + le32_to_cpu ( io_rsp - > OutputOffset ) ,
qi . input_buffer_length ) ) {
2019-03-15 02:07:22 +03:00
rc = - EFAULT ;
goto iqinf_exit ;
}
} else {
pqi = ( struct smb_query_info __user * ) arg ;
qi_rsp = ( struct smb2_query_info_rsp * ) rsp_iov [ 1 ] . iov_base ;
if ( le32_to_cpu ( qi_rsp - > OutputBufferLength ) < qi . input_buffer_length )
qi . input_buffer_length = le32_to_cpu ( qi_rsp - > OutputBufferLength ) ;
if ( copy_to_user ( & pqi - > input_buffer_length , & qi . input_buffer_length ,
sizeof ( qi . input_buffer_length ) ) ) {
rc = - EFAULT ;
goto iqinf_exit ;
}
if ( copy_to_user ( pqi + 1 , qi_rsp - > Buffer , qi . input_buffer_length ) ) {
rc = - EFAULT ;
goto iqinf_exit ;
}
2018-10-08 03:19:58 +03:00
}
iqinf_exit :
2018-10-16 22:47:58 +03:00
kfree ( buffer ) ;
SMB2_open_free ( & rqst [ 0 ] ) ;
2019-03-15 02:07:22 +03:00
if ( qi . flags & PASSTHRU_FSCTL )
SMB2_ioctl_free ( & rqst [ 1 ] ) ;
else
SMB2_query_info_free ( & rqst [ 1 ] ) ;
2018-10-16 22:47:58 +03:00
SMB2_close_free ( & rqst [ 2 ] ) ;
free_rsp_buf ( resp_buftype [ 0 ] , rsp_iov [ 0 ] . iov_base ) ;
free_rsp_buf ( resp_buftype [ 1 ] , rsp_iov [ 1 ] . iov_base ) ;
free_rsp_buf ( resp_buftype [ 2 ] , rsp_iov [ 2 ] . iov_base ) ;
2018-10-08 03:19:58 +03:00
return rc ;
}
2017-02-10 13:33:51 +03:00
static ssize_t
2017-04-04 10:12:04 +03:00
smb2_copychunk_range ( const unsigned int xid ,
2013-11-14 10:05:36 +04:00
struct cifsFileInfo * srcfile ,
struct cifsFileInfo * trgtfile , u64 src_off ,
u64 len , u64 dest_off )
{
int rc ;
unsigned int ret_data_len ;
struct copychunk_ioctl * pcchunk ;
2013-11-17 04:05:28 +04:00
struct copychunk_ioctl_rsp * retbuf = NULL ;
struct cifs_tcon * tcon ;
int chunks_copied = 0 ;
bool chunk_sizes_updated = false ;
2017-02-10 13:33:51 +03:00
ssize_t bytes_written , total_bytes_written = 0 ;
2013-11-14 10:05:36 +04:00
pcchunk = kmalloc ( sizeof ( struct copychunk_ioctl ) , GFP_KERNEL ) ;
if ( pcchunk = = NULL )
return - ENOMEM ;
2019-05-08 22:36:25 +03:00
cifs_dbg ( FYI , " %s: about to call request res key \n " , __func__ ) ;
2013-11-14 10:05:36 +04:00
/* Request a key from the server to identify the source of the copy */
rc = SMB2_request_res_key ( xid , tlink_tcon ( srcfile - > tlink ) ,
srcfile - > fid . persistent_fid ,
srcfile - > fid . volatile_fid , pcchunk ) ;
/* Note: request_res_key sets res_key null only if rc !=0 */
if ( rc )
2013-11-17 04:05:28 +04:00
goto cchunk_out ;
2013-11-14 10:05:36 +04:00
/* For now array only one chunk long, will make more flexible later */
2014-12-11 02:41:15 +03:00
pcchunk - > ChunkCount = cpu_to_le32 ( 1 ) ;
2013-11-14 10:05:36 +04:00
pcchunk - > Reserved = 0 ;
pcchunk - > Reserved2 = 0 ;
2013-11-17 04:05:28 +04:00
tcon = tlink_tcon ( trgtfile - > tlink ) ;
2013-11-14 10:05:36 +04:00
2013-11-17 04:05:28 +04:00
while ( len > 0 ) {
pcchunk - > SourceOffset = cpu_to_le64 ( src_off ) ;
pcchunk - > TargetOffset = cpu_to_le64 ( dest_off ) ;
pcchunk - > Length =
cpu_to_le32 ( min_t ( u32 , len , tcon - > max_bytes_chunk ) ) ;
2013-11-14 10:05:36 +04:00
2013-11-17 04:05:28 +04:00
/* Request server copy to target from src identified by key */
rc = SMB2_ioctl ( xid , tcon , trgtfile - > fid . persistent_fid ,
trgtfile - > fid . volatile_fid , FSCTL_SRV_COPYCHUNK_WRITE ,
2018-01-24 15:46:11 +03:00
true /* is_fsctl */ , ( char * ) pcchunk ,
2019-03-29 06:32:49 +03:00
sizeof ( struct copychunk_ioctl ) , CIFSMaxBufSize ,
( char * * ) & retbuf , & ret_data_len ) ;
2013-11-17 04:05:28 +04:00
if ( rc = = 0 ) {
if ( ret_data_len ! =
sizeof ( struct copychunk_ioctl_rsp ) ) {
cifs_dbg ( VFS , " invalid cchunk response size \n " ) ;
rc = - EIO ;
goto cchunk_out ;
}
if ( retbuf - > TotalBytesWritten = = 0 ) {
cifs_dbg ( FYI , " no bytes copied \n " ) ;
rc = - EIO ;
goto cchunk_out ;
}
/*
* Check if server claimed to write more than we asked
*/
if ( le32_to_cpu ( retbuf - > TotalBytesWritten ) >
le32_to_cpu ( pcchunk - > Length ) ) {
cifs_dbg ( VFS , " invalid copy chunk response \n " ) ;
rc = - EIO ;
goto cchunk_out ;
}
if ( le32_to_cpu ( retbuf - > ChunksWritten ) ! = 1 ) {
cifs_dbg ( VFS , " invalid num chunks written \n " ) ;
rc = - EIO ;
goto cchunk_out ;
}
chunks_copied + + ;
2017-02-10 13:33:51 +03:00
bytes_written = le32_to_cpu ( retbuf - > TotalBytesWritten ) ;
src_off + = bytes_written ;
dest_off + = bytes_written ;
len - = bytes_written ;
total_bytes_written + = bytes_written ;
2013-11-17 04:05:28 +04:00
2017-02-10 13:33:51 +03:00
cifs_dbg ( FYI , " Chunks %d PartialChunk %d Total %zu \n " ,
2013-11-17 04:05:28 +04:00
le32_to_cpu ( retbuf - > ChunksWritten ) ,
le32_to_cpu ( retbuf - > ChunkBytesWritten ) ,
2017-02-10 13:33:51 +03:00
bytes_written ) ;
2013-11-17 04:05:28 +04:00
} else if ( rc = = - EINVAL ) {
if ( ret_data_len ! = sizeof ( struct copychunk_ioctl_rsp ) )
goto cchunk_out ;
cifs_dbg ( FYI , " MaxChunks %d BytesChunk %d MaxCopy %d \n " ,
le32_to_cpu ( retbuf - > ChunksWritten ) ,
le32_to_cpu ( retbuf - > ChunkBytesWritten ) ,
le32_to_cpu ( retbuf - > TotalBytesWritten ) ) ;
/*
* Check if this is the first request using these sizes ,
* ( ie check if copy succeed once with original sizes
* and check if the server gave us different sizes after
* we already updated max sizes on previous request ) .
* if not then why is the server returning an error now
*/
if ( ( chunks_copied ! = 0 ) | | chunk_sizes_updated )
goto cchunk_out ;
/* Check that server is not asking us to grow size */
if ( le32_to_cpu ( retbuf - > ChunkBytesWritten ) <
tcon - > max_bytes_chunk )
tcon - > max_bytes_chunk =
le32_to_cpu ( retbuf - > ChunkBytesWritten ) ;
else
goto cchunk_out ; /* server gave us bogus size */
/* No need to change MaxChunks since already set to 1 */
chunk_sizes_updated = true ;
2015-02-04 16:10:26 +03:00
} else
goto cchunk_out ;
2013-11-17 04:05:28 +04:00
}
2013-11-14 10:05:36 +04:00
2013-11-17 04:05:28 +04:00
cchunk_out :
2013-11-14 10:05:36 +04:00
kfree ( pcchunk ) ;
2016-09-29 12:20:23 +03:00
kfree ( retbuf ) ;
2017-02-10 13:33:51 +03:00
if ( rc )
return rc ;
else
return total_bytes_written ;
2013-11-14 10:05:36 +04:00
}
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 ;
2019-05-08 22:36:25 +03:00
2012-09-19 03:20:29 +04:00
return rsp - > DataOffset ;
}
static unsigned int
2017-11-23 03:38:46 +03:00
smb2_read_data_length ( char * buf , bool in_remaining )
2012-09-19 03:20:29 +04:00
{
struct smb2_read_rsp * rsp = ( struct smb2_read_rsp * ) buf ;
2017-11-23 03:38:46 +03:00
if ( in_remaining )
return le32_to_cpu ( rsp - > DataRemaining ) ;
2012-09-19 03:20:29 +04:00
return le32_to_cpu ( rsp - > DataLength ) ;
}
2012-09-19 03:20:30 +04:00
static int
2014-09-22 14:13:55 +04:00
smb2_sync_read ( const unsigned int xid , struct cifs_fid * pfid ,
2012-09-19 03:20:30 +04:00
struct cifs_io_parms * parms , unsigned int * bytes_read ,
char * * buf , int * buf_type )
{
2014-09-22 14:13:55 +04:00
parms - > persistent_fid = pfid - > persistent_fid ;
parms - > volatile_fid = pfid - > volatile_fid ;
2012-09-19 03:20:30 +04:00
return SMB2_read ( xid , parms , bytes_read , buf , buf_type ) ;
}
2012-09-19 03:20:30 +04:00
static int
2014-09-22 14:13:55 +04:00
smb2_sync_write ( const unsigned int xid , struct cifs_fid * pfid ,
2012-09-19 03:20:30 +04:00
struct cifs_io_parms * parms , unsigned int * written ,
struct kvec * iov , unsigned long nr_segs )
{
2014-09-22 14:13:55 +04:00
parms - > persistent_fid = pfid - > persistent_fid ;
parms - > volatile_fid = pfid - > volatile_fid ;
2012-09-19 03:20:30 +04:00
return SMB2_write ( xid , parms , written , iov , nr_segs ) ;
}
2014-08-14 02:16:29 +04:00
/* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */
static bool smb2_set_sparse ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifsFileInfo * cfile , struct inode * inode , __u8 setsparse )
{
struct cifsInodeInfo * cifsi ;
int rc ;
cifsi = CIFS_I ( inode ) ;
/* if file already sparse don't bother setting sparse again */
if ( ( cifsi - > cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE ) & & setsparse )
return true ; /* already sparse */
if ( ! ( cifsi - > cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE ) & & ! setsparse )
return true ; /* already not sparse */
/*
* Can ' t check for sparse support on share the usual way via the
* FS attribute info ( FILE_SUPPORTS_SPARSE_FILES ) on the share
* since Samba server doesn ' t set the flag on the share , yet
* supports the set sparse FSCTL and returns sparse correctly
* in the file attributes . If we fail setting sparse though we
* mark that server does not support sparse files for this share
* to avoid repeatedly sending the unsupported fsctl to server
* if the file is repeatedly extended .
*/
if ( tcon - > broken_sparse_sup )
return false ;
rc = SMB2_ioctl ( xid , tcon , cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid , FSCTL_SET_SPARSE ,
2018-01-24 15:46:11 +03:00
true /* is_fctl */ ,
2019-03-29 06:32:49 +03:00
& setsparse , 1 , CIFSMaxBufSize , NULL , NULL ) ;
2014-08-14 02:16:29 +04:00
if ( rc ) {
tcon - > broken_sparse_sup = true ;
cifs_dbg ( FYI , " set sparse rc = %d \n " , rc ) ;
return false ;
}
if ( setsparse )
cifsi - > cifsAttrs | = FILE_ATTRIBUTE_SPARSE_FILE ;
else
cifsi - > cifsAttrs & = ( ~ FILE_ATTRIBUTE_SPARSE_FILE ) ;
return true ;
}
2012-09-19 03:20:32 +04:00
static int
smb2_set_file_size ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifsFileInfo * cfile , __u64 size , bool set_alloc )
{
__le64 eof = cpu_to_le64 ( size ) ;
2014-08-12 06:05:25 +04:00
struct inode * inode ;
/*
* If extending file more than one page make sparse . Many Linux fs
* make files sparse by default when extending via ftruncate
*/
2015-03-18 01:25:59 +03:00
inode = d_inode ( cfile - > dentry ) ;
2014-08-12 06:05:25 +04:00
if ( ! set_alloc & & ( size > inode - > i_size + 8192 ) ) {
__u8 set_sparse = 1 ;
2014-08-14 02:16:29 +04:00
/* whether set sparse succeeds or not, extend the file */
smb2_set_sparse ( xid , tcon , cfile , inode , set_sparse ) ;
2014-08-12 06:05:25 +04:00
}
2012-09-19 03:20:32 +04:00
return SMB2_set_eof ( xid , tcon , cfile - > fid . persistent_fid ,
2018-09-03 06:33:47 +03:00
cfile - > fid . volatile_fid , cfile - > pid , & eof ) ;
2012-09-19 03:20:32 +04:00
}
2015-06-28 07:18:36 +03:00
static int
smb2_duplicate_extents ( const unsigned int xid ,
struct cifsFileInfo * srcfile ,
struct cifsFileInfo * trgtfile , u64 src_off ,
u64 len , u64 dest_off )
{
int rc ;
unsigned int ret_data_len ;
struct duplicate_extents_to_file dup_ext_buf ;
struct cifs_tcon * tcon = tlink_tcon ( trgtfile - > tlink ) ;
/* server fileays advertise duplicate extent support with this flag */
if ( ( le32_to_cpu ( tcon - > fsAttrInfo . Attributes ) &
FILE_SUPPORTS_BLOCK_REFCOUNTING ) = = 0 )
return - EOPNOTSUPP ;
dup_ext_buf . VolatileFileHandle = srcfile - > fid . volatile_fid ;
dup_ext_buf . PersistentFileHandle = srcfile - > fid . persistent_fid ;
dup_ext_buf . SourceFileOffset = cpu_to_le64 ( src_off ) ;
dup_ext_buf . TargetFileOffset = cpu_to_le64 ( dest_off ) ;
dup_ext_buf . ByteCount = cpu_to_le64 ( len ) ;
2019-05-08 22:36:25 +03:00
cifs_dbg ( FYI , " Duplicate extents: src off %lld dst off %lld len %lld \n " ,
2015-06-28 07:18:36 +03:00
src_off , dest_off , len ) ;
rc = smb2_set_file_size ( xid , tcon , trgtfile , dest_off + len , false ) ;
if ( rc )
goto duplicate_extents_out ;
rc = SMB2_ioctl ( xid , tcon , trgtfile - > fid . persistent_fid ,
trgtfile - > fid . volatile_fid ,
FSCTL_DUPLICATE_EXTENTS_TO_FILE ,
2018-01-24 15:46:11 +03:00
true /* is_fsctl */ ,
2017-02-28 17:08:41 +03:00
( char * ) & dup_ext_buf ,
2015-06-28 07:18:36 +03:00
sizeof ( struct duplicate_extents_to_file ) ,
2019-03-29 06:32:49 +03:00
CIFSMaxBufSize , NULL ,
2015-06-28 07:18:36 +03:00
& ret_data_len ) ;
if ( ret_data_len > 0 )
2019-05-08 22:36:25 +03:00
cifs_dbg ( FYI , " Non-zero response length in duplicate extents \n " ) ;
2015-06-28 07:18:36 +03:00
duplicate_extents_out :
return rc ;
}
2013-10-15 00:31:32 +04:00
static int
smb2_set_compression ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifsFileInfo * cfile )
{
return SMB2_set_compression ( xid , tcon , cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid ) ;
}
2015-06-24 11:17:02 +03:00
static int
smb3_set_integrity ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifsFileInfo * cfile )
{
struct fsctl_set_integrity_information_req integr_info ;
unsigned int ret_data_len ;
integr_info . ChecksumAlgorithm = cpu_to_le16 ( CHECKSUM_TYPE_UNCHANGED ) ;
integr_info . Flags = 0 ;
integr_info . Reserved = 0 ;
return SMB2_ioctl ( xid , tcon , cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid ,
FSCTL_SET_INTEGRITY_INFORMATION ,
2018-01-24 15:46:11 +03:00
true /* is_fsctl */ ,
2017-02-28 17:08:41 +03:00
( char * ) & integr_info ,
2015-06-24 11:17:02 +03:00
sizeof ( struct fsctl_set_integrity_information_req ) ,
2019-03-29 06:32:49 +03:00
CIFSMaxBufSize , NULL ,
2015-06-24 11:17:02 +03:00
& ret_data_len ) ;
}
2018-08-09 22:33:12 +03:00
/* GMT Token is @GMT-YYYY.MM.DD-HH.MM.SS Unicode which is 48 bytes + null */
# define GMT_TOKEN_SIZE 50
2019-03-29 06:32:49 +03:00
# define MIN_SNAPSHOT_ARRAY_SIZE 16 /* See MS-SMB2 section 3.3.5.15.1 */
2018-08-09 22:33:12 +03:00
/*
* Input buffer contains ( empty ) struct smb_snapshot array with size filled in
* For output see struct SRV_SNAPSHOT_ARRAY in MS - SMB2 section 2.2 .32 .2
*/
2016-10-01 05:14:26 +03:00
static int
smb3_enum_snapshots ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifsFileInfo * cfile , void __user * ioc_buf )
{
char * retbuf = NULL ;
unsigned int ret_data_len = 0 ;
int rc ;
2019-03-29 06:32:49 +03:00
u32 max_response_size ;
2016-10-01 05:14:26 +03:00
struct smb_snapshot_array snapshot_in ;
2019-04-04 08:41:04 +03:00
/*
* On the first query to enumerate the list of snapshots available
* for this volume the buffer begins with 0 ( number of snapshots
* which can be returned is zero since at that point we do not know
* how big the buffer needs to be ) . On the second query ,
* it ( ret_data_len ) is set to number of snapshots so we can
* know to set the maximum response size larger ( see below ) .
*/
2019-03-29 06:32:49 +03:00
if ( get_user ( ret_data_len , ( unsigned int __user * ) ioc_buf ) )
return - EFAULT ;
/*
* Note that for snapshot queries that servers like Azure expect that
* the first query be minimal size ( and just used to get the number / size
* of previous versions ) so response size must be specified as EXACTLY
* sizeof ( struct snapshot_array ) which is 16 when rounded up to multiple
* of eight bytes .
*/
if ( ret_data_len = = 0 )
max_response_size = MIN_SNAPSHOT_ARRAY_SIZE ;
else
max_response_size = CIFSMaxBufSize ;
2016-10-01 05:14:26 +03:00
rc = SMB2_ioctl ( xid , tcon , cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid ,
FSCTL_SRV_ENUMERATE_SNAPSHOTS ,
2018-01-24 15:46:11 +03:00
true /* is_fsctl */ ,
2019-03-29 06:32:49 +03:00
NULL , 0 /* no input data */ , max_response_size ,
2016-10-01 05:14:26 +03:00
( char * * ) & retbuf ,
& ret_data_len ) ;
cifs_dbg ( FYI , " enum snaphots ioctl returned %d and ret buflen is %d \n " ,
rc , ret_data_len ) ;
if ( rc )
return rc ;
if ( ret_data_len & & ( ioc_buf ! = NULL ) & & ( retbuf ! = NULL ) ) {
/* Fixup buffer */
if ( copy_from_user ( & snapshot_in , ioc_buf ,
sizeof ( struct smb_snapshot_array ) ) ) {
rc = - EFAULT ;
kfree ( retbuf ) ;
return rc ;
}
2018-08-09 22:33:12 +03:00
/*
* Check for min size , ie not large enough to fit even one GMT
* token ( snapshot ) . On the first ioctl some users may pass in
* smaller size ( or zero ) to simply get the size of the array
* so the user space caller can allocate sufficient memory
* and retry the ioctl again with larger array size sufficient
* to hold all of the snapshot GMT tokens on the second try .
*/
if ( snapshot_in . snapshot_array_size < GMT_TOKEN_SIZE )
ret_data_len = sizeof ( struct smb_snapshot_array ) ;
/*
* We return struct SRV_SNAPSHOT_ARRAY , followed by
* the snapshot array ( of 50 byte GMT tokens ) each
* representing an available previous version of the data
*/
if ( ret_data_len > ( snapshot_in . snapshot_array_size +
sizeof ( struct smb_snapshot_array ) ) )
ret_data_len = snapshot_in . snapshot_array_size +
sizeof ( struct smb_snapshot_array ) ;
2016-10-01 05:14:26 +03:00
if ( copy_to_user ( ioc_buf , retbuf , ret_data_len ) )
rc = - EFAULT ;
}
kfree ( retbuf ) ;
return rc ;
}
2012-09-19 03:20:33 +04:00
static int
smb2_query_dir_first ( const unsigned int xid , struct cifs_tcon * tcon ,
const char * path , struct cifs_sb_info * cifs_sb ,
struct cifs_fid * fid , __u16 search_flags ,
struct cifs_search_info * srch_inf )
{
__le16 * utf16_path ;
int rc ;
2012-09-19 03:20:33 +04:00
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE ;
2013-07-09 18:20:30 +04:00
struct cifs_open_parms oparms ;
2012-09-19 03:20:33 +04:00
utf16_path = cifs_convert_path_to_utf16 ( path , cifs_sb ) ;
if ( ! utf16_path )
return - ENOMEM ;
2013-07-09 18:20:30 +04:00
oparms . tcon = tcon ;
oparms . desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA ;
oparms . disposition = FILE_OPEN ;
2018-08-28 01:04:13 +03:00
if ( backup_cred ( cifs_sb ) )
oparms . create_options = CREATE_OPEN_BACKUP_INTENT ;
else
oparms . create_options = 0 ;
2013-07-09 18:20:30 +04:00
oparms . fid = fid ;
2013-07-09 18:40:58 +04:00
oparms . reconnect = false ;
2013-07-09 18:20:30 +04:00
2018-06-08 06:21:18 +03:00
rc = SMB2_open ( xid , & oparms , utf16_path , & oplock , NULL , NULL , NULL ) ;
2012-09-19 03:20:33 +04:00
kfree ( utf16_path ) ;
if ( rc ) {
2017-06-07 02:58:58 +03:00
cifs_dbg ( FYI , " open dir failed rc=%d \n " , rc ) ;
2012-09-19 03:20:33 +04:00
return rc ;
}
srch_inf - > entries_in_buffer = 0 ;
smb2: fix missing files in root share directory listing
When mounting a Windows share that is the root of a drive (eg. C$)
the server does not return . and .. directory entries. This results in
the smb2 code path erroneously skipping the 2 first entries.
Pseudo-code of the readdir() code path:
cifs_readdir(struct file, struct dir_context)
initiate_cifs_search <-- if no reponse cached yet
server->ops->query_dir_first
dir_emit_dots
dir_emit <-- adds "." and ".." if we're at pos=0
find_cifs_entry
initiate_cifs_search <-- if pos < start of current response
(restart search)
server->ops->query_dir_next <-- if pos > end of current response
(fetch next search res)
for(...) <-- loops over cur response entries
starting at pos
cifs_filldir <-- skip . and .., emit entry
cifs_fill_dirent
dir_emit
pos++
A) dir_emit_dots() always adds . & ..
and sets the current dir pos to 2 (0 and 1 are done).
Therefore we always want the index_to_find to be 2 regardless of if
the response has . and ..
B) smb1 code initializes index_of_last_entry with a +2 offset
in cifssmb.c CIFSFindFirst():
psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
psrch_inf->entries_in_buffer;
Later in find_cifs_entry() we want to find the next dir entry at pos=2
as a result of (A)
first_entry_in_buffer = cfile->srch_inf.index_of_last_entry -
cfile->srch_inf.entries_in_buffer;
This var is the dir pos that the first entry in the buffer will
have therefore it must be 2 in the first call.
If we don't offset index_of_last_entry by 2 (like in (B)),
first_entry_in_buffer=0 but we were instructed to get pos=2 so this
code in find_cifs_entry() skips the 2 first which is ok for non-root
shares, as it skips . and .. from the response but is not ok for root
shares where the 2 first are actual files
pos_in_buf = index_to_find - first_entry_in_buffer;
// pos_in_buf=2
// we skip 2 first response entries :(
for (i = 0; (i < (pos_in_buf)) && (cur_ent != NULL); i++) {
/* go entry by entry figuring out which is first */
cur_ent = nxt_dir_entry(cur_ent, end_of_smb,
cfile->srch_inf.info_level);
}
C) cifs_filldir() skips . and .. so we can safely ignore them for now.
Sample program:
int main(int argc, char **argv)
{
const char *path = argc >= 2 ? argv[1] : ".";
DIR *dh;
struct dirent *de;
printf("listing path <%s>\n", path);
dh = opendir(path);
if (!dh) {
printf("opendir error %d\n", errno);
return 1;
}
while (1) {
de = readdir(dh);
if (!de) {
if (errno) {
printf("readdir error %d\n", errno);
return 1;
}
printf("end of listing\n");
break;
}
printf("off=%lu <%s>\n", de->d_off, de->d_name);
}
return 0;
}
Before the fix with SMB1 on root shares:
<.> off=1
<..> off=2
<$Recycle.Bin> off=3
<bootmgr> off=4
and on non-root shares:
<.> off=1
<..> off=4 <-- after adding .., the offsets jumps to +2 because
<2536> off=5 we skipped . and .. from response buffer (C)
<411> off=6 but still incremented pos
<file> off=7
<fsx> off=8
Therefore the fix for smb2 is to mimic smb1 behaviour and offset the
index_of_last_entry by 2.
Test results comparing smb1 and smb2 before/after the fix on root
share, non-root shares and on large directories (ie. multi-response
dir listing):
PRE FIX
=======
pre-1-root VS pre-2-root:
ERR pre-2-root is missing [bootmgr, $Recycle.Bin]
pre-1-nonroot VS pre-2-nonroot:
OK~ same files, same order, different offsets
pre-1-nonroot-large VS pre-2-nonroot-large:
OK~ same files, same order, different offsets
POST FIX
========
post-1-root VS post-2-root:
OK same files, same order, same offsets
post-1-nonroot VS post-2-nonroot:
OK same files, same order, same offsets
post-1-nonroot-large VS post-2-nonroot-large:
OK same files, same order, same offsets
REGRESSION?
===========
pre-1-root VS post-1-root:
OK same files, same order, same offsets
pre-1-nonroot VS post-1-nonroot:
OK same files, same order, same offsets
BugLink: https://bugzilla.samba.org/show_bug.cgi?id=13107
Signed-off-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Paulo Alcantara <palcantara@suse.deR>
Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
CC: Stable <stable@vger.kernel.org>
2018-05-17 17:35:07 +03:00
srch_inf - > index_of_last_entry = 2 ;
2012-09-19 03:20:33 +04:00
2013-07-09 18:20:30 +04:00
rc = SMB2_query_directory ( xid , tcon , fid - > persistent_fid ,
fid - > volatile_fid , 0 , srch_inf ) ;
2012-09-19 03:20:33 +04:00
if ( rc ) {
2017-06-07 02:58:58 +03:00
cifs_dbg ( FYI , " query directory failed rc=%d \n " , rc ) ;
2013-07-09 18:20:30 +04:00
SMB2_close ( xid , tcon , fid - > persistent_fid , fid - > volatile_fid ) ;
2012-09-19 03:20:33 +04:00
}
return rc ;
}
static int
smb2_query_dir_next ( const unsigned int xid , struct cifs_tcon * tcon ,
struct cifs_fid * fid , __u16 search_flags ,
struct cifs_search_info * srch_inf )
{
return SMB2_query_directory ( xid , tcon , fid - > persistent_fid ,
fid - > volatile_fid , 0 , srch_inf ) ;
}
static int
smb2_close_dir ( 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:33 +04:00
/*
2019-05-08 22:36:25 +03:00
* If we negotiate SMB2 protocol and get STATUS_PENDING - update
* the number of credits and return true . Otherwise - return false .
*/
2012-09-19 03:20:33 +04:00
static bool
2019-01-24 04:11:16 +03:00
smb2_is_status_pending ( char * buf , struct TCP_Server_Info * server )
2012-09-19 03:20:33 +04:00
{
2018-06-01 03:53:06 +03:00
struct smb2_sync_hdr * shdr = ( struct smb2_sync_hdr * ) buf ;
2012-09-19 03:20:33 +04:00
2016-10-25 01:33:04 +03:00
if ( shdr - > Status ! = STATUS_PENDING )
2012-09-19 03:20:33 +04:00
return false ;
2019-01-24 04:11:16 +03:00
if ( shdr - > CreditRequest ) {
2012-09-19 03:20:33 +04:00
spin_lock ( & server - > req_lock ) ;
2016-10-25 01:33:04 +03:00
server - > credits + = le16_to_cpu ( shdr - > CreditRequest ) ;
2012-09-19 03:20:33 +04:00
spin_unlock ( & server - > req_lock ) ;
wake_up ( & server - > request_q ) ;
}
return true ;
}
2017-07-09 00:32:00 +03:00
static bool
smb2_is_session_expired ( char * buf )
{
2018-06-01 03:53:06 +03:00
struct smb2_sync_hdr * shdr = ( struct smb2_sync_hdr * ) buf ;
2017-07-09 00:32:00 +03:00
2018-05-24 11:47:31 +03:00
if ( shdr - > Status ! = STATUS_NETWORK_SESSION_EXPIRED & &
shdr - > Status ! = STATUS_USER_SESSION_DELETED )
2017-07-09 00:32:00 +03:00
return false ;
2018-07-30 22:23:58 +03:00
trace_smb3_ses_expired ( shdr - > TreeId , shdr - > SessionId ,
le16_to_cpu ( shdr - > Command ) ,
le64_to_cpu ( shdr - > MessageId ) ) ;
2018-05-24 11:47:31 +03:00
cifs_dbg ( FYI , " Session expired or deleted \n " ) ;
2018-07-30 22:23:58 +03:00
2017-07-09 00:32:00 +03:00
return true ;
}
2012-09-19 03:20:33 +04:00
static int
smb2_oplock_response ( struct cifs_tcon * tcon , struct cifs_fid * fid ,
struct cifsInodeInfo * cinode )
{
2012-09-19 17:22:45 +04:00
if ( tcon - > ses - > server - > capabilities & SMB2_GLOBAL_CAP_LEASING )
return SMB2_lease_break ( 0 , tcon , cinode - > lease_key ,
smb2_get_lease_state ( cinode ) ) ;
2012-09-19 03:20:33 +04:00
return SMB2_oplock_break ( 0 , tcon , fid - > persistent_fid ,
fid - > volatile_fid ,
2013-09-05 13:01:06 +04:00
CIFS_CACHE_READ ( cinode ) ? 1 : 0 ) ;
2012-09-19 03:20:33 +04:00
}
2018-09-03 06:33:41 +03:00
void
2018-08-08 08:07:49 +03:00
smb2_set_related ( struct smb_rqst * rqst )
{
struct smb2_sync_hdr * shdr ;
shdr = ( struct smb2_sync_hdr * ) ( rqst - > rq_iov [ 0 ] . iov_base ) ;
2019-07-16 03:41:46 +03:00
if ( shdr = = NULL ) {
cifs_dbg ( FYI , " shdr NULL in smb2_set_related \n " ) ;
return ;
}
2018-08-08 08:07:49 +03:00
shdr - > Flags | = SMB2_FLAGS_RELATED_OPERATIONS ;
}
char smb2_padding [ 7 ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
2018-09-03 06:33:41 +03:00
void
2018-12-31 06:43:40 +03:00
smb2_set_next_command ( struct cifs_tcon * tcon , struct smb_rqst * rqst )
2018-08-08 08:07:49 +03:00
{
struct smb2_sync_hdr * shdr ;
2018-12-31 06:43:40 +03:00
struct cifs_ses * ses = tcon - > ses ;
struct TCP_Server_Info * server = ses - > server ;
2018-08-08 08:07:49 +03:00
unsigned long len = smb_rqst_len ( server , rqst ) ;
2018-12-31 06:43:40 +03:00
int i , num_padding ;
2018-08-08 08:07:49 +03:00
2019-07-16 03:41:46 +03:00
shdr = ( struct smb2_sync_hdr * ) ( rqst - > rq_iov [ 0 ] . iov_base ) ;
if ( shdr = = NULL ) {
cifs_dbg ( FYI , " shdr NULL in smb2_set_next_command \n " ) ;
return ;
}
2018-08-08 08:07:49 +03:00
/* SMB headers in a compound are 8 byte aligned. */
2018-12-31 06:43:40 +03:00
/* No padding needed */
if ( ! ( len & 7 ) )
goto finished ;
num_padding = 8 - ( len & 7 ) ;
if ( ! smb3_encryption_required ( tcon ) ) {
/*
* If we do not have encryption then we can just add an extra
* iov for the padding .
*/
rqst - > rq_iov [ rqst - > rq_nvec ] . iov_base = smb2_padding ;
rqst - > rq_iov [ rqst - > rq_nvec ] . iov_len = num_padding ;
rqst - > rq_nvec + + ;
len + = num_padding ;
} else {
/*
* We can not add a small padding iov for the encryption case
* because the encryption framework can not handle the padding
* iovs .
* We have to flatten this into a single buffer and add
* the padding to it .
*/
for ( i = 1 ; i < rqst - > rq_nvec ; i + + ) {
memcpy ( rqst - > rq_iov [ 0 ] . iov_base +
rqst - > rq_iov [ 0 ] . iov_len ,
rqst - > rq_iov [ i ] . iov_base ,
rqst - > rq_iov [ i ] . iov_len ) ;
rqst - > rq_iov [ 0 ] . iov_len + = rqst - > rq_iov [ i ] . iov_len ;
2018-12-19 02:49:05 +03:00
}
2018-12-31 06:43:40 +03:00
memset ( rqst - > rq_iov [ 0 ] . iov_base + rqst - > rq_iov [ 0 ] . iov_len ,
0 , num_padding ) ;
rqst - > rq_iov [ 0 ] . iov_len + = num_padding ;
len + = num_padding ;
rqst - > rq_nvec = 1 ;
2018-08-08 08:07:49 +03:00
}
2018-12-31 06:43:40 +03:00
finished :
2018-08-08 08:07:49 +03:00
shdr - > NextCommand = cpu_to_le32 ( len ) ;
}
2018-12-21 07:03:04 +03:00
/*
* Passes the query info response back to the caller on success .
* Caller need to free this with free_rsp_buf ( ) .
*/
2018-11-27 02:52:04 +03:00
int
2018-12-21 07:03:04 +03:00
smb2_query_info_compound ( const unsigned int xid , struct cifs_tcon * tcon ,
__le16 * utf16_path , u32 desired_access ,
u32 class , u32 type , u32 output_len ,
2018-11-27 02:52:04 +03:00
struct kvec * rsp , int * buftype ,
struct cifs_sb_info * cifs_sb )
2012-09-19 03:20:34 +04:00
{
2018-12-21 07:03:04 +03:00
struct cifs_ses * ses = tcon - > ses ;
int flags = 0 ;
2018-08-08 08:07:49 +03:00
struct smb_rqst rqst [ 3 ] ;
int resp_buftype [ 3 ] ;
struct kvec rsp_iov [ 3 ] ;
2018-08-21 04:49:21 +03:00
struct kvec open_iov [ SMB2_CREATE_IOV_SIZE ] ;
2018-08-08 08:07:49 +03:00
struct kvec qi_iov [ 1 ] ;
struct kvec close_iov [ 1 ] ;
2012-09-19 03:20:34 +04:00
u8 oplock = SMB2_OPLOCK_LEVEL_NONE ;
2013-07-09 18:20:30 +04:00
struct cifs_open_parms oparms ;
struct cifs_fid fid ;
2018-08-08 08:07:49 +03:00
int rc ;
if ( smb3_encryption_required ( tcon ) )
flags | = CIFS_TRANSFORM_REQ ;
memset ( rqst , 0 , sizeof ( rqst ) ) ;
2018-09-03 06:33:41 +03:00
resp_buftype [ 0 ] = resp_buftype [ 1 ] = resp_buftype [ 2 ] = CIFS_NO_BUFFER ;
2018-08-08 08:07:49 +03:00
memset ( rsp_iov , 0 , sizeof ( rsp_iov ) ) ;
memset ( & open_iov , 0 , sizeof ( open_iov ) ) ;
rqst [ 0 ] . rq_iov = open_iov ;
2018-08-21 04:49:21 +03:00
rqst [ 0 ] . rq_nvec = SMB2_CREATE_IOV_SIZE ;
2013-07-09 18:20:30 +04:00
oparms . tcon = tcon ;
2018-12-21 07:03:04 +03:00
oparms . desired_access = desired_access ;
2013-07-09 18:20:30 +04:00
oparms . disposition = FILE_OPEN ;
2018-11-27 02:52:04 +03:00
if ( cifs_sb & & backup_cred ( cifs_sb ) )
oparms . create_options = CREATE_OPEN_BACKUP_INTENT ;
else
oparms . create_options = 0 ;
2013-07-09 18:20:30 +04:00
oparms . fid = & fid ;
2013-07-09 18:40:58 +04:00
oparms . reconnect = false ;
2012-09-19 03:20:34 +04:00
2018-12-21 07:03:04 +03:00
rc = SMB2_open_init ( tcon , & rqst [ 0 ] , & oplock , & oparms , utf16_path ) ;
2012-09-19 03:20:34 +04:00
if ( rc )
2018-12-21 07:03:04 +03:00
goto qic_exit ;
2018-12-31 06:43:40 +03:00
smb2_set_next_command ( tcon , & rqst [ 0 ] ) ;
2018-08-08 08:07:49 +03:00
memset ( & qi_iov , 0 , sizeof ( qi_iov ) ) ;
rqst [ 1 ] . rq_iov = qi_iov ;
rqst [ 1 ] . rq_nvec = 1 ;
rc = SMB2_query_info_init ( tcon , & rqst [ 1 ] , COMPOUND_FID , COMPOUND_FID ,
2018-12-21 07:03:04 +03:00
class , type , 0 ,
output_len , 0 ,
2018-10-08 03:19:58 +03:00
NULL ) ;
2018-08-08 08:07:49 +03:00
if ( rc )
2018-12-21 07:03:04 +03:00
goto qic_exit ;
2018-12-31 06:43:40 +03:00
smb2_set_next_command ( tcon , & rqst [ 1 ] ) ;
2018-08-08 08:07:49 +03:00
smb2_set_related ( & rqst [ 1 ] ) ;
memset ( & close_iov , 0 , sizeof ( close_iov ) ) ;
rqst [ 2 ] . rq_iov = close_iov ;
rqst [ 2 ] . rq_nvec = 1 ;
rc = SMB2_close_init ( tcon , & rqst [ 2 ] , COMPOUND_FID , COMPOUND_FID ) ;
if ( rc )
2018-12-21 07:03:04 +03:00
goto qic_exit ;
2018-08-08 08:07:49 +03:00
smb2_set_related ( & rqst [ 2 ] ) ;
rc = compound_send_recv ( xid , ses , flags , 3 , rqst ,
resp_buftype , rsp_iov ) ;
2018-11-27 02:52:04 +03:00
if ( rc ) {
free_rsp_buf ( resp_buftype [ 1 ] , rsp_iov [ 1 ] . iov_base ) ;
2018-12-21 07:03:04 +03:00
goto qic_exit ;
2018-11-27 02:52:04 +03:00
}
2018-12-21 07:03:04 +03:00
* rsp = rsp_iov [ 1 ] ;
* buftype = resp_buftype [ 1 ] ;
qic_exit :
SMB2_open_free ( & rqst [ 0 ] ) ;
SMB2_query_info_free ( & rqst [ 1 ] ) ;
SMB2_close_free ( & rqst [ 2 ] ) ;
free_rsp_buf ( resp_buftype [ 0 ] , rsp_iov [ 0 ] . iov_base ) ;
free_rsp_buf ( resp_buftype [ 2 ] , rsp_iov [ 2 ] . iov_base ) ;
return rc ;
}
static int
smb2_queryfs ( const unsigned int xid , struct cifs_tcon * tcon ,
struct kstatfs * buf )
{
struct smb2_query_info_rsp * rsp ;
struct smb2_fs_full_size_info * info = NULL ;
__le16 utf16_path = 0 ; /* Null - open root of share */
struct kvec rsp_iov = { NULL , 0 } ;
int buftype = CIFS_NO_BUFFER ;
int rc ;
rc = smb2_query_info_compound ( xid , tcon , & utf16_path ,
FILE_READ_ATTRIBUTES ,
FS_FULL_SIZE_INFORMATION ,
SMB2_O_INFO_FILESYSTEM ,
sizeof ( struct smb2_fs_full_size_info ) ,
2018-11-27 02:52:04 +03:00
& rsp_iov , & buftype , NULL ) ;
2018-08-08 08:07:49 +03:00
if ( rc )
goto qfs_exit ;
2018-12-21 07:03:04 +03:00
rsp = ( struct smb2_query_info_rsp * ) rsp_iov . iov_base ;
2012-09-19 03:20:34 +04:00
buf - > f_type = SMB2_MAGIC_NUMBER ;
2018-08-08 08:07:49 +03:00
info = ( struct smb2_fs_full_size_info * ) (
le16_to_cpu ( rsp - > OutputBufferOffset ) + ( char * ) rsp ) ;
rc = smb2_validate_iov ( le16_to_cpu ( rsp - > OutputBufferOffset ) ,
le32_to_cpu ( rsp - > OutputBufferLength ) ,
2018-12-21 07:03:04 +03:00
& rsp_iov ,
2018-08-08 08:07:49 +03:00
sizeof ( struct smb2_fs_full_size_info ) ) ;
if ( ! rc )
smb2_copy_fs_info_to_kstatfs ( info , buf ) ;
qfs_exit :
2018-12-21 07:03:04 +03:00
free_rsp_buf ( buftype , rsp_iov . iov_base ) ;
2012-09-19 03:20:34 +04:00
return rc ;
}
2018-06-25 07:28:12 +03:00
static int
smb311_queryfs ( const unsigned int xid , struct cifs_tcon * tcon ,
struct kstatfs * buf )
{
int rc ;
__le16 srch_path = 0 ; /* Null - open root of share */
u8 oplock = SMB2_OPLOCK_LEVEL_NONE ;
struct cifs_open_parms oparms ;
struct cifs_fid fid ;
if ( ! tcon - > posix_extensions )
return smb2_queryfs ( xid , tcon , buf ) ;
oparms . tcon = tcon ;
oparms . desired_access = FILE_READ_ATTRIBUTES ;
oparms . disposition = FILE_OPEN ;
oparms . create_options = 0 ;
oparms . fid = & fid ;
oparms . reconnect = false ;
rc = SMB2_open ( xid , & oparms , & srch_path , & oplock , NULL , NULL , NULL ) ;
if ( rc )
return rc ;
rc = SMB311_posix_qfs_info ( xid , tcon , fid . persistent_fid ,
fid . volatile_fid , buf ) ;
buf - > f_type = SMB2_MAGIC_NUMBER ;
SMB2_close ( xid , tcon , fid . persistent_fid , fid . volatile_fid ) ;
return rc ;
}
2012-09-19 17:22:43 +04:00
static bool
smb2_compare_fids ( struct cifsFileInfo * ob1 , struct cifsFileInfo * ob2 )
{
return ob1 - > fid . persistent_fid = = ob2 - > fid . persistent_fid & &
ob1 - > fid . volatile_fid = = ob2 - > fid . volatile_fid ;
}
2012-09-19 17:22:43 +04:00
static int
smb2_mand_lock ( const unsigned int xid , struct cifsFileInfo * cfile , __u64 offset ,
__u64 length , __u32 type , int lock , int unlock , bool wait )
{
if ( unlock & & ! lock )
type = SMB2_LOCKFLAG_UNLOCK ;
return SMB2_lock ( xid , tlink_tcon ( cfile - > tlink ) ,
cfile - > fid . persistent_fid , cfile - > fid . volatile_fid ,
current - > tgid , length , offset , type , wait ) ;
}
2012-09-19 17:22:44 +04:00
static void
smb2_get_lease_key ( struct inode * inode , struct cifs_fid * fid )
{
memcpy ( fid - > lease_key , CIFS_I ( inode ) - > lease_key , SMB2_LEASE_KEY_SIZE ) ;
}
static void
smb2_set_lease_key ( struct inode * inode , struct cifs_fid * fid )
{
memcpy ( CIFS_I ( inode ) - > lease_key , fid - > lease_key , SMB2_LEASE_KEY_SIZE ) ;
}
static void
smb2_new_lease_key ( struct cifs_fid * fid )
{
2016-09-22 08:39:34 +03:00
generate_random_uuid ( fid - > lease_key ) ;
2012-09-19 17:22:44 +04:00
}
2017-02-13 18:16:49 +03:00
static int
smb2_get_dfs_refer ( const unsigned int xid , struct cifs_ses * ses ,
const char * search_name ,
struct dfs_info3_param * * target_nodes ,
unsigned int * num_of_nodes ,
const struct nls_table * nls_codepage , int remap )
{
int rc ;
__le16 * utf16_path = NULL ;
int utf16_path_len = 0 ;
struct cifs_tcon * tcon ;
struct fsctl_get_dfs_referral_req * dfs_req = NULL ;
struct get_dfs_referral_rsp * dfs_rsp = NULL ;
u32 dfs_req_size = 0 , dfs_rsp_size = 0 ;
2019-05-08 22:36:25 +03:00
cifs_dbg ( FYI , " %s: path: %s \n " , __func__ , search_name ) ;
2017-02-13 18:16:49 +03:00
/*
2018-01-24 15:46:11 +03:00
* Try to use the IPC tcon , otherwise just use any
2017-02-13 18:16:49 +03:00
*/
2018-01-24 15:46:11 +03:00
tcon = ses - > tcon_ipc ;
if ( tcon = = NULL ) {
spin_lock ( & cifs_tcp_ses_lock ) ;
tcon = list_first_entry_or_null ( & ses - > tcon_list ,
struct cifs_tcon ,
tcon_list ) ;
if ( tcon )
tcon - > tc_count + + ;
spin_unlock ( & cifs_tcp_ses_lock ) ;
}
if ( tcon = = NULL ) {
2017-02-13 18:16:49 +03:00
cifs_dbg ( VFS , " session %p has no tcon available for a dfs referral request \n " ,
ses ) ;
rc = - ENOTCONN ;
goto out ;
}
utf16_path = cifs_strndup_to_utf16 ( search_name , PATH_MAX ,
& utf16_path_len ,
nls_codepage , remap ) ;
if ( ! utf16_path ) {
rc = - ENOMEM ;
goto out ;
}
dfs_req_size = sizeof ( * dfs_req ) + utf16_path_len ;
dfs_req = kzalloc ( dfs_req_size , GFP_KERNEL ) ;
if ( ! dfs_req ) {
rc = - ENOMEM ;
goto out ;
}
/* Highest DFS referral version understood */
dfs_req - > MaxReferralLevel = DFS_VERSION ;
/* Path to resolve in an UTF-16 null-terminated string */
memcpy ( dfs_req - > RequestFileName , utf16_path , utf16_path_len ) ;
do {
rc = SMB2_ioctl ( xid , tcon , NO_FILE_ID , NO_FILE_ID ,
FSCTL_DFS_GET_REFERRALS ,
2018-01-24 15:46:11 +03:00
true /* is_fsctl */ ,
2019-03-29 06:32:49 +03:00
( char * ) dfs_req , dfs_req_size , CIFSMaxBufSize ,
2017-02-13 18:16:49 +03:00
( char * * ) & dfs_rsp , & dfs_rsp_size ) ;
} while ( rc = = - EAGAIN ) ;
if ( rc ) {
2018-03-22 07:16:36 +03:00
if ( ( rc ! = - ENOENT ) & & ( rc ! = - EOPNOTSUPP ) )
2019-05-08 22:36:25 +03:00
cifs_dbg ( VFS , " ioctl error in %s rc=%d \n " , __func__ , rc ) ;
2017-02-13 18:16:49 +03:00
goto out ;
}
rc = parse_dfs_referrals ( dfs_rsp , dfs_rsp_size ,
num_of_nodes , target_nodes ,
nls_codepage , remap , search_name ,
true /* is_unicode */ ) ;
if ( rc ) {
2019-05-08 22:36:25 +03:00
cifs_dbg ( VFS , " parse error in %s rc=%d \n " , __func__ , rc ) ;
2017-02-13 18:16:49 +03:00
goto out ;
}
out :
2018-01-24 15:46:11 +03:00
if ( tcon & & ! tcon - > ipc ) {
/* ipc tcons are not refcounted */
2017-02-13 18:16:49 +03:00
spin_lock ( & cifs_tcp_ses_lock ) ;
tcon - > tc_count - - ;
spin_unlock ( & cifs_tcp_ses_lock ) ;
}
kfree ( utf16_path ) ;
kfree ( dfs_req ) ;
kfree ( dfs_rsp ) ;
return rc ;
}
2019-06-27 07:57:02 +03:00
2019-06-28 10:04:18 +03:00
static int
parse_reparse_posix ( struct reparse_posix_data * symlink_buf ,
u32 plen , char * * target_path ,
struct cifs_sb_info * cifs_sb )
{
unsigned int len ;
/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */
len = le16_to_cpu ( symlink_buf - > ReparseDataLength ) ;
if ( le64_to_cpu ( symlink_buf - > InodeType ) ! = NFS_SPECFILE_LNK ) {
cifs_dbg ( VFS , " %lld not a supported symlink type \n " ,
le64_to_cpu ( symlink_buf - > InodeType ) ) ;
return - EOPNOTSUPP ;
}
* target_path = cifs_strndup_from_utf16 (
symlink_buf - > PathBuffer ,
len , true , cifs_sb - > local_nls ) ;
if ( ! ( * target_path ) )
return - ENOMEM ;
convert_delimiter ( * target_path , ' / ' ) ;
cifs_dbg ( FYI , " %s: target path: %s \n " , __func__ , * target_path ) ;
return 0 ;
}
2019-06-27 07:57:02 +03:00
static int
parse_reparse_symlink ( struct reparse_symlink_data_buffer * symlink_buf ,
u32 plen , char * * target_path ,
struct cifs_sb_info * cifs_sb )
{
unsigned int sub_len ;
unsigned int sub_offset ;
2019-06-28 10:04:18 +03:00
/* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */
2019-06-27 07:57:02 +03:00
sub_offset = le16_to_cpu ( symlink_buf - > SubstituteNameOffset ) ;
sub_len = le16_to_cpu ( symlink_buf - > SubstituteNameLength ) ;
if ( sub_offset + 20 > plen | |
sub_offset + sub_len + 20 > plen ) {
cifs_dbg ( VFS , " srv returned malformed symlink buffer \n " ) ;
return - EIO ;
}
* target_path = cifs_strndup_from_utf16 (
symlink_buf - > PathBuffer + sub_offset ,
sub_len , true , cifs_sb - > local_nls ) ;
if ( ! ( * target_path ) )
return - ENOMEM ;
convert_delimiter ( * target_path , ' / ' ) ;
cifs_dbg ( FYI , " %s: target path: %s \n " , __func__ , * target_path ) ;
return 0 ;
}
2019-06-28 10:04:18 +03:00
static int
2019-07-07 00:45:42 +03:00
parse_reparse_point ( struct reparse_data_buffer * buf ,
u32 plen , char * * target_path ,
struct cifs_sb_info * cifs_sb )
2019-06-28 10:04:18 +03:00
{
2019-07-07 00:45:42 +03:00
if ( plen < sizeof ( struct reparse_data_buffer ) ) {
cifs_dbg ( VFS , " reparse buffer is too small. Must be "
" at least 8 bytes but was %d \n " , plen ) ;
return - EIO ;
}
2019-06-28 10:04:18 +03:00
2019-07-07 00:45:42 +03:00
if ( plen < le16_to_cpu ( buf - > ReparseDataLength ) +
sizeof ( struct reparse_data_buffer ) ) {
cifs_dbg ( VFS , " srv returned invalid reparse buf "
" length: %d \n " , plen ) ;
return - EIO ;
}
2019-06-28 10:04:18 +03:00
2019-07-07 00:45:42 +03:00
/* See MS-FSCC 2.1.2 */
switch ( le32_to_cpu ( buf - > ReparseTag ) ) {
case IO_REPARSE_TAG_NFS :
return parse_reparse_posix (
( struct reparse_posix_data * ) buf ,
plen , target_path , cifs_sb ) ;
case IO_REPARSE_TAG_SYMLINK :
return parse_reparse_symlink (
( struct reparse_symlink_data_buffer * ) buf ,
plen , target_path , cifs_sb ) ;
default :
cifs_dbg ( VFS , " srv returned unknown symlink buffer "
" tag:0x%08x \n " , le32_to_cpu ( buf - > ReparseTag ) ) ;
return - EOPNOTSUPP ;
}
2019-06-28 10:04:18 +03:00
}
2016-07-24 10:37:38 +03:00
# define SMB2_SYMLINK_STRUCT_SIZE \
( sizeof ( struct smb2_err_rsp ) - 1 + sizeof ( struct smb2_symlink_err_rsp ) )
2013-08-14 19:25:21 +04:00
static int
smb2_query_symlink ( const unsigned int xid , struct cifs_tcon * tcon ,
2019-04-10 01:44:46 +03:00
struct cifs_sb_info * cifs_sb , const char * full_path ,
char * * target_path , bool is_reparse_point )
2013-08-14 19:25:21 +04:00
{
int rc ;
2019-04-10 01:44:46 +03:00
__le16 * utf16_path = NULL ;
2013-08-14 19:25:21 +04:00
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE ;
struct cifs_open_parms oparms ;
struct cifs_fid fid ;
2018-04-13 02:03:19 +03:00
struct kvec err_iov = { NULL , 0 } ;
2018-06-08 06:21:18 +03:00
struct smb2_err_rsp * err_buf = NULL ;
2013-08-14 19:25:21 +04:00
struct smb2_symlink_err_rsp * symlink ;
2016-07-24 10:37:38 +03:00
unsigned int sub_len ;
unsigned int sub_offset ;
unsigned int print_len ;
unsigned int print_offset ;
2019-04-10 01:44:46 +03:00
int flags = 0 ;
struct smb_rqst rqst [ 3 ] ;
int resp_buftype [ 3 ] ;
struct kvec rsp_iov [ 3 ] ;
struct kvec open_iov [ SMB2_CREATE_IOV_SIZE ] ;
struct kvec io_iov [ SMB2_IOCTL_IOV_SIZE ] ;
struct kvec close_iov [ 1 ] ;
struct smb2_create_rsp * create_rsp ;
struct smb2_ioctl_rsp * ioctl_rsp ;
2019-06-27 07:57:02 +03:00
struct reparse_data_buffer * reparse_buf ;
2019-04-10 01:44:46 +03:00
u32 plen ;
2013-08-14 19:25:21 +04:00
cifs_dbg ( FYI , " %s: path: %s \n " , __func__ , full_path ) ;
2019-06-27 07:57:02 +03:00
* target_path = NULL ;
2019-04-10 01:44:46 +03:00
if ( smb3_encryption_required ( tcon ) )
flags | = CIFS_TRANSFORM_REQ ;
memset ( rqst , 0 , sizeof ( rqst ) ) ;
resp_buftype [ 0 ] = resp_buftype [ 1 ] = resp_buftype [ 2 ] = CIFS_NO_BUFFER ;
memset ( rsp_iov , 0 , sizeof ( rsp_iov ) ) ;
2013-08-14 19:25:21 +04:00
utf16_path = cifs_convert_path_to_utf16 ( full_path , cifs_sb ) ;
if ( ! utf16_path )
return - ENOMEM ;
2019-04-10 01:44:46 +03:00
/* Open */
memset ( & open_iov , 0 , sizeof ( open_iov ) ) ;
rqst [ 0 ] . rq_iov = open_iov ;
rqst [ 0 ] . rq_nvec = SMB2_CREATE_IOV_SIZE ;
memset ( & oparms , 0 , sizeof ( oparms ) ) ;
2013-08-14 19:25:21 +04:00
oparms . tcon = tcon ;
oparms . desired_access = FILE_READ_ATTRIBUTES ;
oparms . disposition = FILE_OPEN ;
2019-04-10 01:44:46 +03:00
2018-08-28 01:04:13 +03:00
if ( backup_cred ( cifs_sb ) )
oparms . create_options = CREATE_OPEN_BACKUP_INTENT ;
else
oparms . create_options = 0 ;
2019-04-10 01:44:46 +03:00
if ( is_reparse_point )
oparms . create_options = OPEN_REPARSE_POINT ;
2013-08-14 19:25:21 +04:00
oparms . fid = & fid ;
oparms . reconnect = false ;
2019-04-10 01:44:46 +03:00
rc = SMB2_open_init ( tcon , & rqst [ 0 ] , & oplock , & oparms , utf16_path ) ;
if ( rc )
goto querty_exit ;
smb2_set_next_command ( tcon , & rqst [ 0 ] ) ;
/* IOCTL */
memset ( & io_iov , 0 , sizeof ( io_iov ) ) ;
rqst [ 1 ] . rq_iov = io_iov ;
rqst [ 1 ] . rq_nvec = SMB2_IOCTL_IOV_SIZE ;
rc = SMB2_ioctl_init ( tcon , & rqst [ 1 ] , fid . persistent_fid ,
fid . volatile_fid , FSCTL_GET_REPARSE_POINT ,
true /* is_fctl */ , NULL , 0 , CIFSMaxBufSize ) ;
if ( rc )
goto querty_exit ;
smb2_set_next_command ( tcon , & rqst [ 1 ] ) ;
smb2_set_related ( & rqst [ 1 ] ) ;
/* Close */
memset ( & close_iov , 0 , sizeof ( close_iov ) ) ;
rqst [ 2 ] . rq_iov = close_iov ;
rqst [ 2 ] . rq_nvec = 1 ;
rc = SMB2_close_init ( tcon , & rqst [ 2 ] , COMPOUND_FID , COMPOUND_FID ) ;
if ( rc )
goto querty_exit ;
smb2_set_related ( & rqst [ 2 ] ) ;
rc = compound_send_recv ( xid , tcon - > ses , flags , 3 , rqst ,
resp_buftype , rsp_iov ) ;
create_rsp = rsp_iov [ 0 ] . iov_base ;
if ( create_rsp & & create_rsp - > sync_hdr . Status )
err_iov = rsp_iov [ 0 ] ;
ioctl_rsp = rsp_iov [ 1 ] . iov_base ;
/*
* Open was successful and we got an ioctl response .
*/
if ( ( rc = = 0 ) & & ( is_reparse_point ) ) {
/* See MS-FSCC 2.3.23 */
2019-06-27 07:57:02 +03:00
reparse_buf = ( struct reparse_data_buffer * )
( ( char * ) ioctl_rsp +
le32_to_cpu ( ioctl_rsp - > OutputOffset ) ) ;
2019-04-10 01:44:46 +03:00
plen = le32_to_cpu ( ioctl_rsp - > OutputCount ) ;
if ( plen + le32_to_cpu ( ioctl_rsp - > OutputOffset ) >
rsp_iov [ 1 ] . iov_len ) {
2019-06-27 07:57:02 +03:00
cifs_dbg ( VFS , " srv returned invalid ioctl len: %d \n " ,
plen ) ;
rc = - EIO ;
goto querty_exit ;
}
2019-07-07 00:45:42 +03:00
rc = parse_reparse_point ( reparse_buf , plen , target_path ,
cifs_sb ) ;
2019-04-10 01:44:46 +03:00
goto querty_exit ;
}
2018-04-13 18:13:29 +03:00
if ( ! rc | | ! err_iov . iov_base ) {
2018-06-08 06:21:18 +03:00
rc = - ENOENT ;
2019-04-10 01:44:46 +03:00
goto querty_exit ;
2013-08-14 19:25:21 +04:00
}
2016-07-24 10:37:38 +03:00
2018-04-13 02:03:19 +03:00
err_buf = err_iov . iov_base ;
2016-07-24 10:37:38 +03:00
if ( le32_to_cpu ( err_buf - > ByteCount ) < sizeof ( struct smb2_symlink_err_rsp ) | |
2018-06-01 03:53:07 +03:00
err_iov . iov_len < SMB2_SYMLINK_STRUCT_SIZE ) {
2019-07-09 11:41:11 +03:00
rc = - EINVAL ;
goto querty_exit ;
}
symlink = ( struct smb2_symlink_err_rsp * ) err_buf - > ErrorData ;
if ( le32_to_cpu ( symlink - > SymLinkErrorTag ) ! = SYMLINK_ERROR_TAG | |
le32_to_cpu ( symlink - > ReparseTag ) ! = IO_REPARSE_TAG_SYMLINK ) {
rc = - EINVAL ;
2018-06-08 06:21:18 +03:00
goto querty_exit ;
2016-07-24 10:37:38 +03:00
}
2013-08-14 19:25:21 +04:00
/* open must fail on symlink - reset rc */
rc = 0 ;
sub_len = le16_to_cpu ( symlink - > SubstituteNameLength ) ;
sub_offset = le16_to_cpu ( symlink - > SubstituteNameOffset ) ;
2016-07-24 10:37:38 +03:00
print_len = le16_to_cpu ( symlink - > PrintNameLength ) ;
print_offset = le16_to_cpu ( symlink - > PrintNameOffset ) ;
2018-06-01 03:53:07 +03:00
if ( err_iov . iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offset + sub_len ) {
2019-07-09 11:41:11 +03:00
rc = - EINVAL ;
2018-06-08 06:21:18 +03:00
goto querty_exit ;
2016-07-24 10:37:38 +03:00
}
2018-06-01 03:53:07 +03:00
if ( err_iov . iov_len <
SMB2_SYMLINK_STRUCT_SIZE + print_offset + print_len ) {
2019-07-09 11:41:11 +03:00
rc = - EINVAL ;
2018-06-08 06:21:18 +03:00
goto querty_exit ;
2016-07-24 10:37:38 +03:00
}
2013-08-14 19:25:21 +04:00
* target_path = cifs_strndup_from_utf16 (
( char * ) symlink - > PathBuffer + sub_offset ,
sub_len , true , cifs_sb - > local_nls ) ;
if ( ! ( * target_path ) ) {
2018-06-08 06:21:18 +03:00
rc = - ENOMEM ;
goto querty_exit ;
2013-08-14 19:25:21 +04:00
}
convert_delimiter ( * target_path , ' / ' ) ;
cifs_dbg ( FYI , " %s: target path: %s \n " , __func__ , * target_path ) ;
2018-06-08 06:21:18 +03:00
querty_exit :
2019-04-10 01:44:46 +03:00
cifs_dbg ( FYI , " query symlink rc %d \n " , rc ) ;
2013-08-14 19:25:21 +04:00
kfree ( utf16_path ) ;
2019-04-10 01:44:46 +03:00
SMB2_open_free ( & rqst [ 0 ] ) ;
SMB2_ioctl_free ( & rqst [ 1 ] ) ;
SMB2_close_free ( & rqst [ 2 ] ) ;
free_rsp_buf ( resp_buftype [ 0 ] , rsp_iov [ 0 ] . iov_base ) ;
free_rsp_buf ( resp_buftype [ 1 ] , rsp_iov [ 1 ] . iov_base ) ;
free_rsp_buf ( resp_buftype [ 2 ] , rsp_iov [ 2 ] . iov_base ) ;
2013-08-14 19:25:21 +04:00
return rc ;
}
2017-06-23 06:52:05 +03:00
static struct cifs_ntsd *
get_smb2_acl_by_fid ( struct cifs_sb_info * cifs_sb ,
const struct cifs_fid * cifsfid , u32 * pacllen )
{
struct cifs_ntsd * pntsd = NULL ;
unsigned int xid ;
int rc = - EOPNOTSUPP ;
struct tcon_link * tlink = cifs_sb_tlink ( cifs_sb ) ;
if ( IS_ERR ( tlink ) )
return ERR_CAST ( tlink ) ;
xid = get_xid ( ) ;
cifs_dbg ( FYI , " trying to get acl \n " ) ;
rc = SMB2_query_acl ( xid , tlink_tcon ( tlink ) , cifsfid - > persistent_fid ,
cifsfid - > volatile_fid , ( void * * ) & pntsd , pacllen ) ;
free_xid ( xid ) ;
cifs_put_tlink ( tlink ) ;
cifs_dbg ( FYI , " %s: rc = %d ACL len %d \n " , __func__ , rc , * pacllen ) ;
if ( rc )
return ERR_PTR ( rc ) ;
return pntsd ;
}
static struct cifs_ntsd *
get_smb2_acl_by_path ( struct cifs_sb_info * cifs_sb ,
const char * path , u32 * pacllen )
{
struct cifs_ntsd * pntsd = NULL ;
u8 oplock = SMB2_OPLOCK_LEVEL_NONE ;
unsigned int xid ;
int rc ;
struct cifs_tcon * tcon ;
struct tcon_link * tlink = cifs_sb_tlink ( cifs_sb ) ;
struct cifs_fid fid ;
struct cifs_open_parms oparms ;
__le16 * utf16_path ;
cifs_dbg ( FYI , " get smb3 acl for path %s \n " , path ) ;
if ( IS_ERR ( tlink ) )
return ERR_CAST ( tlink ) ;
tcon = tlink_tcon ( tlink ) ;
xid = get_xid ( ) ;
if ( backup_cred ( cifs_sb ) )
2017-07-05 15:47:34 +03:00
oparms . create_options = CREATE_OPEN_BACKUP_INTENT ;
2017-06-23 06:52:05 +03:00
else
oparms . create_options = 0 ;
utf16_path = cifs_convert_path_to_utf16 ( path , cifs_sb ) ;
2018-05-19 10:04:55 +03:00
if ( ! utf16_path ) {
rc = - ENOMEM ;
free_xid ( xid ) ;
return ERR_PTR ( rc ) ;
}
2017-06-23 06:52:05 +03:00
oparms . tcon = tcon ;
oparms . desired_access = READ_CONTROL ;
oparms . disposition = FILE_OPEN ;
oparms . fid = & fid ;
oparms . reconnect = false ;
2018-06-08 06:21:18 +03:00
rc = SMB2_open ( xid , & oparms , utf16_path , & oplock , NULL , NULL , NULL ) ;
2017-06-23 06:52:05 +03:00
kfree ( utf16_path ) ;
if ( ! rc ) {
rc = SMB2_query_acl ( xid , tlink_tcon ( tlink ) , fid . persistent_fid ,
fid . volatile_fid , ( void * * ) & pntsd , pacllen ) ;
SMB2_close ( xid , tcon , fid . persistent_fid , fid . volatile_fid ) ;
}
cifs_put_tlink ( tlink ) ;
free_xid ( xid ) ;
cifs_dbg ( FYI , " %s: rc = %d ACL len %d \n " , __func__ , rc , * pacllen ) ;
if ( rc )
return ERR_PTR ( rc ) ;
return pntsd ;
}
2017-06-29 06:37:32 +03:00
static int
set_smb2_acl ( struct cifs_ntsd * pnntsd , __u32 acllen ,
struct inode * inode , const char * path , int aclflag )
{
u8 oplock = SMB2_OPLOCK_LEVEL_NONE ;
unsigned int xid ;
int rc , access_flags = 0 ;
struct cifs_tcon * tcon ;
struct cifs_sb_info * cifs_sb = CIFS_SB ( inode - > i_sb ) ;
struct tcon_link * tlink = cifs_sb_tlink ( cifs_sb ) ;
struct cifs_fid fid ;
struct cifs_open_parms oparms ;
__le16 * utf16_path ;
cifs_dbg ( FYI , " set smb3 acl for path %s \n " , path ) ;
if ( IS_ERR ( tlink ) )
return PTR_ERR ( tlink ) ;
tcon = tlink_tcon ( tlink ) ;
xid = get_xid ( ) ;
if ( backup_cred ( cifs_sb ) )
oparms . create_options = CREATE_OPEN_BACKUP_INTENT ;
else
oparms . create_options = 0 ;
if ( aclflag = = CIFS_ACL_OWNER | | aclflag = = CIFS_ACL_GROUP )
access_flags = WRITE_OWNER ;
else
access_flags = WRITE_DAC ;
utf16_path = cifs_convert_path_to_utf16 ( path , cifs_sb ) ;
2018-05-19 10:04:55 +03:00
if ( ! utf16_path ) {
rc = - ENOMEM ;
free_xid ( xid ) ;
return rc ;
}
2017-06-29 06:37:32 +03:00
oparms . tcon = tcon ;
oparms . desired_access = access_flags ;
oparms . disposition = FILE_OPEN ;
oparms . path = path ;
oparms . fid = & fid ;
oparms . reconnect = false ;
2018-06-08 06:21:18 +03:00
rc = SMB2_open ( xid , & oparms , utf16_path , & oplock , NULL , NULL , NULL ) ;
2017-06-29 06:37:32 +03:00
kfree ( utf16_path ) ;
if ( ! rc ) {
rc = SMB2_set_acl ( xid , tlink_tcon ( tlink ) , fid . persistent_fid ,
fid . volatile_fid , pnntsd , acllen , aclflag ) ;
SMB2_close ( xid , tcon , fid . persistent_fid , fid . volatile_fid ) ;
}
cifs_put_tlink ( tlink ) ;
free_xid ( xid ) ;
return rc ;
}
2017-06-23 06:52:05 +03:00
/* Retrieve an ACL from the server */
static struct cifs_ntsd *
get_smb2_acl ( struct cifs_sb_info * cifs_sb ,
struct inode * inode , const char * path ,
u32 * pacllen )
{
struct cifs_ntsd * pntsd = NULL ;
struct cifsFileInfo * open_file = NULL ;
if ( inode )
open_file = find_readable_file ( CIFS_I ( inode ) , true ) ;
if ( ! open_file )
return get_smb2_acl_by_path ( cifs_sb , path , pacllen ) ;
pntsd = get_smb2_acl_by_fid ( cifs_sb , & open_file - > fid , pacllen ) ;
cifsFileInfo_put ( open_file ) ;
return pntsd ;
}
2014-08-18 03:16:40 +04:00
static long smb3_zero_range ( struct file * file , struct cifs_tcon * tcon ,
loff_t offset , loff_t len , bool keep_size )
{
2019-03-13 07:37:49 +03:00
struct cifs_ses * ses = tcon - > ses ;
2014-08-18 03:16:40 +04:00
struct inode * inode ;
struct cifsInodeInfo * cifsi ;
struct cifsFileInfo * cfile = file - > private_data ;
struct file_zero_data_information fsctl_buf ;
long rc ;
unsigned int xid ;
2019-03-13 07:37:49 +03:00
__le64 eof ;
2014-08-18 03:16:40 +04:00
xid = get_xid ( ) ;
2015-03-18 01:25:59 +03:00
inode = d_inode ( cfile - > dentry ) ;
2014-08-18 03:16:40 +04:00
cifsi = CIFS_I ( inode ) ;
2019-05-08 22:36:25 +03:00
trace_smb3_zero_enter ( xid , cfile - > fid . persistent_fid , tcon - > tid ,
2019-03-13 09:41:49 +03:00
ses - > Suid , offset , len ) ;
2014-08-18 03:16:40 +04:00
/* if file not oplocked can't be sure whether asking to extend size */
if ( ! CIFS_CACHE_READ ( cifsi ) )
2018-05-19 10:04:55 +03:00
if ( keep_size = = false ) {
rc = - EOPNOTSUPP ;
2019-03-13 09:41:49 +03:00
trace_smb3_zero_err ( xid , cfile - > fid . persistent_fid ,
tcon - > tid , ses - > Suid , offset , len , rc ) ;
2018-05-19 10:04:55 +03:00
free_xid ( xid ) ;
return rc ;
}
2014-08-18 03:16:40 +04:00
2019-05-09 08:09:37 +03:00
cifs_dbg ( FYI , " Offset %lld len %lld \n " , offset , len ) ;
2014-08-18 03:16:40 +04:00
fsctl_buf . FileOffset = cpu_to_le64 ( offset ) ;
fsctl_buf . BeyondFinalZero = cpu_to_le64 ( offset + len ) ;
2019-05-02 08:52:57 +03:00
rc = SMB2_ioctl ( xid , tcon , cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid , FSCTL_SET_ZERO_DATA , true ,
( char * ) & fsctl_buf ,
sizeof ( struct file_zero_data_information ) ,
0 , NULL , NULL ) ;
2019-03-13 07:37:49 +03:00
if ( rc )
goto zero_range_exit ;
/*
* do we also need to change the size of the file ?
*/
if ( keep_size = = false & & i_size_read ( inode ) < offset + len ) {
eof = cpu_to_le64 ( offset + len ) ;
2019-05-02 08:52:57 +03:00
rc = SMB2_set_eof ( xid , tcon , cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid , cfile - > pid , & eof ) ;
2019-03-13 07:37:49 +03:00
}
zero_range_exit :
2014-08-18 03:16:40 +04:00
free_xid ( xid ) ;
2019-03-13 09:41:49 +03:00
if ( rc )
trace_smb3_zero_err ( xid , cfile - > fid . persistent_fid , tcon - > tid ,
ses - > Suid , offset , len , rc ) ;
else
trace_smb3_zero_done ( xid , cfile - > fid . persistent_fid , tcon - > tid ,
ses - > Suid , offset , len ) ;
2014-08-18 03:16:40 +04:00
return rc ;
}
2014-08-17 17:38:47 +04:00
static long smb3_punch_hole ( struct file * file , struct cifs_tcon * tcon ,
loff_t offset , loff_t len )
{
struct inode * inode ;
struct cifsInodeInfo * cifsi ;
struct cifsFileInfo * cfile = file - > private_data ;
struct file_zero_data_information fsctl_buf ;
long rc ;
unsigned int xid ;
__u8 set_sparse = 1 ;
xid = get_xid ( ) ;
2015-03-18 01:25:59 +03:00
inode = d_inode ( cfile - > dentry ) ;
2014-08-17 17:38:47 +04:00
cifsi = CIFS_I ( inode ) ;
/* Need to make file sparse, if not already, before freeing range. */
/* Consider adding equivalent for compressed since it could also work */
2018-05-19 10:04:55 +03:00
if ( ! smb2_set_sparse ( xid , tcon , cfile , inode , set_sparse ) ) {
rc = - EOPNOTSUPP ;
free_xid ( xid ) ;
return rc ;
}
2014-08-17 17:38:47 +04:00
2019-05-08 22:36:25 +03:00
cifs_dbg ( FYI , " Offset %lld len %lld \n " , offset , len ) ;
2014-08-17 17:38:47 +04:00
fsctl_buf . FileOffset = cpu_to_le64 ( offset ) ;
fsctl_buf . BeyondFinalZero = cpu_to_le64 ( offset + len ) ;
rc = SMB2_ioctl ( xid , tcon , cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid , FSCTL_SET_ZERO_DATA ,
2018-01-24 15:46:11 +03:00
true /* is_fctl */ , ( char * ) & fsctl_buf ,
2019-03-29 06:32:49 +03:00
sizeof ( struct file_zero_data_information ) ,
CIFSMaxBufSize , NULL , NULL ) ;
2014-08-17 17:38:47 +04:00
free_xid ( xid ) ;
return rc ;
}
2014-10-19 02:01:15 +04:00
static long smb3_simple_falloc ( struct file * file , struct cifs_tcon * tcon ,
loff_t off , loff_t len , bool keep_size )
{
struct inode * inode ;
struct cifsInodeInfo * cifsi ;
struct cifsFileInfo * cfile = file - > private_data ;
long rc = - EOPNOTSUPP ;
unsigned int xid ;
2019-03-14 17:08:48 +03:00
__le64 eof ;
2014-10-19 02:01:15 +04:00
xid = get_xid ( ) ;
2015-03-18 01:25:59 +03:00
inode = d_inode ( cfile - > dentry ) ;
2014-10-19 02:01:15 +04:00
cifsi = CIFS_I ( inode ) ;
2019-03-13 09:41:49 +03:00
trace_smb3_falloc_enter ( xid , cfile - > fid . persistent_fid , tcon - > tid ,
tcon - > ses - > Suid , off , len ) ;
2014-10-19 02:01:15 +04:00
/* if file not oplocked can't be sure whether asking to extend size */
if ( ! CIFS_CACHE_READ ( cifsi ) )
2018-05-19 10:04:55 +03:00
if ( keep_size = = false ) {
2019-03-13 09:41:49 +03:00
trace_smb3_falloc_err ( xid , cfile - > fid . persistent_fid ,
tcon - > tid , tcon - > ses - > Suid , off , len , rc ) ;
2018-05-19 10:04:55 +03:00
free_xid ( xid ) ;
return rc ;
}
2014-10-19 02:01:15 +04:00
/*
* Files are non - sparse by default so falloc may be a no - op
* Must check if file sparse . If not sparse , and not extending
* then no need to do anything since file already allocated
*/
if ( ( cifsi - > cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE ) = = 0 ) {
if ( keep_size = = true )
2018-05-19 10:04:55 +03:00
rc = 0 ;
2014-10-19 02:01:15 +04:00
/* check if extending file */
else if ( i_size_read ( inode ) > = off + len )
/* not extending file and already not sparse */
2018-05-19 10:04:55 +03:00
rc = 0 ;
2014-10-19 02:01:15 +04:00
/* BB: in future add else clause to extend file */
else
2018-05-19 10:04:55 +03:00
rc = - EOPNOTSUPP ;
2019-03-13 09:41:49 +03:00
if ( rc )
trace_smb3_falloc_err ( xid , cfile - > fid . persistent_fid ,
tcon - > tid , tcon - > ses - > Suid , off , len , rc ) ;
else
trace_smb3_falloc_done ( xid , cfile - > fid . persistent_fid ,
tcon - > tid , tcon - > ses - > Suid , off , len ) ;
2018-05-19 10:04:55 +03:00
free_xid ( xid ) ;
return rc ;
2014-10-19 02:01:15 +04:00
}
if ( ( keep_size = = true ) | | ( i_size_read ( inode ) > = off + len ) ) {
/*
* Check if falloc starts within first few pages of file
* and ends within a few pages of the end of file to
* ensure that most of file is being forced to be
* fallocated now . If so then setting whole file sparse
* ie potentially making a few extra pages at the beginning
* or end of the file non - sparse via set_sparse is harmless .
*/
2018-05-19 10:04:55 +03:00
if ( ( off > 8192 ) | | ( off + len + 8192 < i_size_read ( inode ) ) ) {
rc = - EOPNOTSUPP ;
2019-03-13 09:41:49 +03:00
trace_smb3_falloc_err ( xid , cfile - > fid . persistent_fid ,
tcon - > tid , tcon - > ses - > Suid , off , len , rc ) ;
2018-05-19 10:04:55 +03:00
free_xid ( xid ) ;
return rc ;
}
2014-10-19 02:01:15 +04:00
2019-03-14 17:08:48 +03:00
smb2_set_sparse ( xid , tcon , cfile , inode , false ) ;
rc = 0 ;
} else {
smb2_set_sparse ( xid , tcon , cfile , inode , false ) ;
rc = 0 ;
if ( i_size_read ( inode ) < off + len ) {
eof = cpu_to_le64 ( off + len ) ;
rc = SMB2_set_eof ( xid , tcon , cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid , cfile - > pid ,
& eof ) ;
}
2014-10-19 02:01:15 +04:00
}
2019-03-13 09:41:49 +03:00
if ( rc )
trace_smb3_falloc_err ( xid , cfile - > fid . persistent_fid , tcon - > tid ,
tcon - > ses - > Suid , off , len , rc ) ;
else
trace_smb3_falloc_done ( xid , cfile - > fid . persistent_fid , tcon - > tid ,
tcon - > ses - > Suid , off , len ) ;
2014-10-19 02:01:15 +04:00
free_xid ( xid ) ;
return rc ;
}
2019-05-15 00:17:02 +03:00
static loff_t smb3_llseek ( struct file * file , struct cifs_tcon * tcon , loff_t offset , int whence )
{
struct cifsFileInfo * wrcfile , * cfile = file - > private_data ;
struct cifsInodeInfo * cifsi ;
struct inode * inode ;
int rc = 0 ;
struct file_allocated_range_buffer in_data , * out_data = NULL ;
u32 out_data_len ;
unsigned int xid ;
if ( whence ! = SEEK_HOLE & & whence ! = SEEK_DATA )
return generic_file_llseek ( file , offset , whence ) ;
inode = d_inode ( cfile - > dentry ) ;
cifsi = CIFS_I ( inode ) ;
if ( offset < 0 | | offset > = i_size_read ( inode ) )
return - ENXIO ;
xid = get_xid ( ) ;
/*
* We need to be sure that all dirty pages are written as they
* might fill holes on the server .
* Note that we also MUST flush any written pages since at least
* some servers ( Windows2016 ) will not reflect recent writes in
* QUERY_ALLOCATED_RANGES until SMB2_flush is called .
*/
wrcfile = find_writable_file ( cifsi , false ) ;
if ( wrcfile ) {
filemap_write_and_wait ( inode - > i_mapping ) ;
smb2_flush_file ( xid , tcon , & wrcfile - > fid ) ;
cifsFileInfo_put ( wrcfile ) ;
}
if ( ! ( cifsi - > cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE ) ) {
if ( whence = = SEEK_HOLE )
offset = i_size_read ( inode ) ;
goto lseek_exit ;
}
in_data . file_offset = cpu_to_le64 ( offset ) ;
in_data . length = cpu_to_le64 ( i_size_read ( inode ) ) ;
rc = SMB2_ioctl ( xid , tcon , cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid ,
FSCTL_QUERY_ALLOCATED_RANGES , true ,
( char * ) & in_data , sizeof ( in_data ) ,
sizeof ( struct file_allocated_range_buffer ) ,
( char * * ) & out_data , & out_data_len ) ;
if ( rc = = - E2BIG )
rc = 0 ;
if ( rc )
goto lseek_exit ;
if ( whence = = SEEK_HOLE & & out_data_len = = 0 )
goto lseek_exit ;
if ( whence = = SEEK_DATA & & out_data_len = = 0 ) {
rc = - ENXIO ;
goto lseek_exit ;
}
if ( out_data_len < sizeof ( struct file_allocated_range_buffer ) ) {
rc = - EINVAL ;
goto lseek_exit ;
}
if ( whence = = SEEK_DATA ) {
offset = le64_to_cpu ( out_data - > file_offset ) ;
goto lseek_exit ;
}
if ( offset < le64_to_cpu ( out_data - > file_offset ) )
goto lseek_exit ;
offset = le64_to_cpu ( out_data - > file_offset ) + le64_to_cpu ( out_data - > length ) ;
lseek_exit :
free_xid ( xid ) ;
kfree ( out_data ) ;
if ( ! rc )
return vfs_setpos ( file , offset , inode - > i_sb - > s_maxbytes ) ;
else
return rc ;
}
2019-04-25 09:45:29 +03:00
static int smb3_fiemap ( struct cifs_tcon * tcon ,
struct cifsFileInfo * cfile ,
struct fiemap_extent_info * fei , u64 start , u64 len )
{
unsigned int xid ;
struct file_allocated_range_buffer in_data , * out_data ;
u32 out_data_len ;
int i , num , rc , flags , last_blob ;
u64 next ;
if ( fiemap_check_flags ( fei , FIEMAP_FLAG_SYNC ) )
return - EBADR ;
xid = get_xid ( ) ;
again :
in_data . file_offset = cpu_to_le64 ( start ) ;
in_data . length = cpu_to_le64 ( len ) ;
rc = SMB2_ioctl ( xid , tcon , cfile - > fid . persistent_fid ,
cfile - > fid . volatile_fid ,
FSCTL_QUERY_ALLOCATED_RANGES , true ,
( char * ) & in_data , sizeof ( in_data ) ,
1024 * sizeof ( struct file_allocated_range_buffer ) ,
( char * * ) & out_data , & out_data_len ) ;
if ( rc = = - E2BIG ) {
last_blob = 0 ;
rc = 0 ;
} else
last_blob = 1 ;
if ( rc )
goto out ;
if ( out_data_len < sizeof ( struct file_allocated_range_buffer ) ) {
rc = - EINVAL ;
goto out ;
}
if ( out_data_len % sizeof ( struct file_allocated_range_buffer ) ) {
rc = - EINVAL ;
goto out ;
}
num = out_data_len / sizeof ( struct file_allocated_range_buffer ) ;
for ( i = 0 ; i < num ; i + + ) {
flags = 0 ;
if ( i = = num - 1 & & last_blob )
flags | = FIEMAP_EXTENT_LAST ;
rc = fiemap_fill_next_extent ( fei ,
le64_to_cpu ( out_data [ i ] . file_offset ) ,
le64_to_cpu ( out_data [ i ] . file_offset ) ,
le64_to_cpu ( out_data [ i ] . length ) ,
flags ) ;
if ( rc < 0 )
goto out ;
if ( rc = = 1 ) {
rc = 0 ;
goto out ;
}
}
if ( ! last_blob ) {
next = le64_to_cpu ( out_data [ num - 1 ] . file_offset ) +
le64_to_cpu ( out_data [ num - 1 ] . length ) ;
len = len - ( next - start ) ;
start = next ;
goto again ;
}
out :
free_xid ( xid ) ;
kfree ( out_data ) ;
return rc ;
}
2014-10-19 02:01:15 +04:00
2014-08-17 17:38:47 +04:00
static long smb3_fallocate ( struct file * file , struct cifs_tcon * tcon , int mode ,
loff_t off , loff_t len )
{
/* KEEP_SIZE already checked for by do_fallocate */
if ( mode & FALLOC_FL_PUNCH_HOLE )
return smb3_punch_hole ( file , tcon , off , len ) ;
2014-08-18 03:16:40 +04:00
else if ( mode & FALLOC_FL_ZERO_RANGE ) {
if ( mode & FALLOC_FL_KEEP_SIZE )
return smb3_zero_range ( file , tcon , off , len , true ) ;
return smb3_zero_range ( file , tcon , off , len , false ) ;
2014-10-19 02:01:15 +04:00
} else if ( mode = = FALLOC_FL_KEEP_SIZE )
return smb3_simple_falloc ( file , tcon , off , len , true ) ;
else if ( mode = = 0 )
return smb3_simple_falloc ( file , tcon , off , len , false ) ;
2014-08-17 17:38:47 +04:00
return - EOPNOTSUPP ;
}
2014-03-11 20:11:47 +04:00
static void
smb2_downgrade_oplock ( struct TCP_Server_Info * server ,
struct cifsInodeInfo * cinode , bool set_level2 )
{
if ( set_level2 )
server - > ops - > set_oplock_level ( cinode , SMB2_OPLOCK_LEVEL_II ,
0 , NULL ) ;
else
server - > ops - > set_oplock_level ( cinode , 0 , 0 , NULL ) ;
}
2019-02-14 02:43:08 +03:00
static void
smb21_downgrade_oplock ( struct TCP_Server_Info * server ,
struct cifsInodeInfo * cinode , bool set_level2 )
{
server - > ops - > set_oplock_level ( cinode ,
set_level2 ? SMB2_LEASE_READ_CACHING_HE :
0 , 0 , NULL ) ;
}
2013-09-05 16:11:28 +04:00
static void
2013-09-05 21:30:16 +04:00
smb2_set_oplock_level ( struct cifsInodeInfo * cinode , __u32 oplock ,
unsigned int epoch , bool * purge_cache )
2013-09-05 16:11:28 +04:00
{
oplock & = 0xFF ;
if ( oplock = = SMB2_OPLOCK_LEVEL_NOCHANGE )
return ;
if ( oplock = = SMB2_OPLOCK_LEVEL_BATCH ) {
2013-09-05 21:30:16 +04:00
cinode - > oplock = CIFS_CACHE_RHW_FLG ;
2013-09-05 16:11:28 +04:00
cifs_dbg ( FYI , " Batch Oplock granted on inode %p \n " ,
& cinode - > vfs_inode ) ;
} else if ( oplock = = SMB2_OPLOCK_LEVEL_EXCLUSIVE ) {
2013-09-05 21:30:16 +04:00
cinode - > oplock = CIFS_CACHE_RW_FLG ;
2013-09-05 16:11:28 +04:00
cifs_dbg ( FYI , " Exclusive Oplock granted on inode %p \n " ,
& cinode - > vfs_inode ) ;
} else if ( oplock = = SMB2_OPLOCK_LEVEL_II ) {
cinode - > oplock = CIFS_CACHE_READ_FLG ;
cifs_dbg ( FYI , " Level II Oplock granted on inode %p \n " ,
& cinode - > vfs_inode ) ;
} else
cinode - > oplock = 0 ;
}
static void
2013-09-05 21:30:16 +04:00
smb21_set_oplock_level ( struct cifsInodeInfo * cinode , __u32 oplock ,
unsigned int epoch , bool * purge_cache )
2013-09-05 16:11:28 +04:00
{
char message [ 5 ] = { 0 } ;
2019-05-07 18:16:40 +03:00
unsigned int new_oplock = 0 ;
2013-09-05 16:11:28 +04:00
oplock & = 0xFF ;
if ( oplock = = SMB2_OPLOCK_LEVEL_NOCHANGE )
return ;
if ( oplock & SMB2_LEASE_READ_CACHING_HE ) {
2019-05-07 18:16:40 +03:00
new_oplock | = CIFS_CACHE_READ_FLG ;
2013-09-05 16:11:28 +04:00
strcat ( message , " R " ) ;
}
if ( oplock & SMB2_LEASE_HANDLE_CACHING_HE ) {
2019-05-07 18:16:40 +03:00
new_oplock | = CIFS_CACHE_HANDLE_FLG ;
2013-09-05 16:11:28 +04:00
strcat ( message , " H " ) ;
}
if ( oplock & SMB2_LEASE_WRITE_CACHING_HE ) {
2019-05-07 18:16:40 +03:00
new_oplock | = CIFS_CACHE_WRITE_FLG ;
2013-09-05 16:11:28 +04:00
strcat ( message , " W " ) ;
}
2019-05-07 18:16:40 +03:00
if ( ! new_oplock )
strncpy ( message , " None " , sizeof ( message ) ) ;
cinode - > oplock = new_oplock ;
2013-09-05 16:11:28 +04:00
cifs_dbg ( FYI , " %s Lease granted on inode %p \n " , message ,
& cinode - > vfs_inode ) ;
}
2013-09-05 21:30:16 +04:00
static void
smb3_set_oplock_level ( struct cifsInodeInfo * cinode , __u32 oplock ,
unsigned int epoch , bool * purge_cache )
{
unsigned int old_oplock = cinode - > oplock ;
smb21_set_oplock_level ( cinode , oplock , epoch , purge_cache ) ;
if ( purge_cache ) {
* purge_cache = false ;
if ( old_oplock = = CIFS_CACHE_READ_FLG ) {
if ( cinode - > oplock = = CIFS_CACHE_READ_FLG & &
( epoch - cinode - > epoch > 0 ) )
* purge_cache = true ;
else if ( cinode - > oplock = = CIFS_CACHE_RH_FLG & &
( epoch - cinode - > epoch > 1 ) )
* purge_cache = true ;
else if ( cinode - > oplock = = CIFS_CACHE_RHW_FLG & &
( epoch - cinode - > epoch > 1 ) )
* purge_cache = true ;
else if ( cinode - > oplock = = 0 & &
( epoch - cinode - > epoch > 0 ) )
* purge_cache = true ;
} else if ( old_oplock = = CIFS_CACHE_RH_FLG ) {
if ( cinode - > oplock = = CIFS_CACHE_RH_FLG & &
( epoch - cinode - > epoch > 0 ) )
* purge_cache = true ;
else if ( cinode - > oplock = = CIFS_CACHE_RHW_FLG & &
( epoch - cinode - > epoch > 1 ) )
* purge_cache = true ;
}
cinode - > epoch = epoch ;
}
}
2013-09-05 16:11:28 +04:00
static bool
smb2_is_read_op ( __u32 oplock )
{
return oplock = = SMB2_OPLOCK_LEVEL_II ;
}
static bool
smb21_is_read_op ( __u32 oplock )
{
return ( oplock & SMB2_LEASE_READ_CACHING_HE ) & &
! ( oplock & SMB2_LEASE_WRITE_CACHING_HE ) ;
}
2013-09-04 13:44:05 +04:00
static __le32
map_oplock_to_lease ( u8 oplock )
{
if ( oplock = = SMB2_OPLOCK_LEVEL_EXCLUSIVE )
return SMB2_LEASE_WRITE_CACHING | SMB2_LEASE_READ_CACHING ;
else if ( oplock = = SMB2_OPLOCK_LEVEL_II )
return SMB2_LEASE_READ_CACHING ;
else if ( oplock = = SMB2_OPLOCK_LEVEL_BATCH )
return SMB2_LEASE_HANDLE_CACHING | SMB2_LEASE_READ_CACHING |
SMB2_LEASE_WRITE_CACHING ;
return 0 ;
}
2013-09-04 13:07:41 +04:00
static char *
smb2_create_lease_buf ( u8 * lease_key , u8 oplock )
{
struct create_lease * buf ;
buf = kzalloc ( sizeof ( struct create_lease ) , GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
2018-07-05 16:10:02 +03:00
memcpy ( & buf - > lcontext . LeaseKey , lease_key , SMB2_LEASE_KEY_SIZE ) ;
2013-09-04 13:44:05 +04:00
buf - > lcontext . LeaseState = map_oplock_to_lease ( oplock ) ;
2013-09-04 13:07:41 +04:00
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
( struct create_lease , lcontext ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( sizeof ( struct lease_context ) ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct create_lease , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
2014-05-14 16:29:40 +04:00
/* SMB2_CREATE_REQUEST_LEASE is "RqLs" */
2013-09-04 13:07:41 +04:00
buf - > Name [ 0 ] = ' R ' ;
buf - > Name [ 1 ] = ' q ' ;
buf - > Name [ 2 ] = ' L ' ;
buf - > Name [ 3 ] = ' s ' ;
return ( char * ) buf ;
}
2013-09-04 13:44:05 +04:00
static char *
smb3_create_lease_buf ( u8 * lease_key , u8 oplock )
{
struct create_lease_v2 * buf ;
buf = kzalloc ( sizeof ( struct create_lease_v2 ) , GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
2018-07-05 16:10:02 +03:00
memcpy ( & buf - > lcontext . LeaseKey , lease_key , SMB2_LEASE_KEY_SIZE ) ;
2013-09-04 13:44:05 +04:00
buf - > lcontext . LeaseState = map_oplock_to_lease ( oplock ) ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
( struct create_lease_v2 , lcontext ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( sizeof ( struct lease_context_v2 ) ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct create_lease_v2 , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
2014-05-14 16:29:40 +04:00
/* SMB2_CREATE_REQUEST_LEASE is "RqLs" */
2013-09-04 13:44:05 +04:00
buf - > Name [ 0 ] = ' R ' ;
buf - > Name [ 1 ] = ' q ' ;
buf - > Name [ 2 ] = ' L ' ;
buf - > Name [ 3 ] = ' s ' ;
return ( char * ) buf ;
}
2013-09-05 20:16:45 +04:00
static __u8
2018-04-26 17:10:18 +03:00
smb2_parse_lease_buf ( void * buf , unsigned int * epoch , char * lease_key )
2013-09-05 20:16:45 +04:00
{
struct create_lease * lc = ( struct create_lease * ) buf ;
2013-09-05 21:30:16 +04:00
* epoch = 0 ; /* not used */
2013-09-05 20:16:45 +04:00
if ( lc - > lcontext . LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS )
return SMB2_OPLOCK_LEVEL_NOCHANGE ;
return le32_to_cpu ( lc - > lcontext . LeaseState ) ;
}
2013-09-04 13:44:05 +04:00
static __u8
2018-04-26 17:10:18 +03:00
smb3_parse_lease_buf ( void * buf , unsigned int * epoch , char * lease_key )
2013-09-04 13:44:05 +04:00
{
struct create_lease_v2 * lc = ( struct create_lease_v2 * ) buf ;
2013-09-05 21:30:16 +04:00
* epoch = le16_to_cpu ( lc - > lcontext . Epoch ) ;
2013-09-04 13:44:05 +04:00
if ( lc - > lcontext . LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS )
return SMB2_OPLOCK_LEVEL_NOCHANGE ;
2018-04-26 17:10:18 +03:00
if ( lease_key )
2018-07-05 16:10:02 +03:00
memcpy ( lease_key , & lc - > lcontext . LeaseKey , SMB2_LEASE_KEY_SIZE ) ;
2013-09-04 13:44:05 +04:00
return le32_to_cpu ( lc - > lcontext . LeaseState ) ;
}
2014-06-22 11:03:22 +04:00
static unsigned int
smb2_wp_retry_size ( struct inode * inode )
{
return min_t ( unsigned int , CIFS_SB ( inode - > i_sb ) - > wsize ,
SMB2_MAX_BUFFER_SIZE ) ;
}
2014-08-18 20:49:57 +04:00
static bool
smb2_dir_needs_close ( struct cifsFileInfo * cfile )
{
return ! cfile - > invalidHandle ;
}
2016-11-04 02:47:37 +03:00
static void
2018-06-01 03:53:02 +03:00
fill_transform_hdr ( struct smb2_transform_hdr * tr_hdr , unsigned int orig_len ,
2019-06-07 23:16:10 +03:00
struct smb_rqst * old_rq , __le16 cipher_type )
2016-11-04 02:47:37 +03:00
{
struct smb2_sync_hdr * shdr =
2018-06-12 01:00:58 +03:00
( struct smb2_sync_hdr * ) old_rq - > rq_iov [ 0 ] . iov_base ;
2016-11-04 02:47:37 +03:00
memset ( tr_hdr , 0 , sizeof ( struct smb2_transform_hdr ) ) ;
tr_hdr - > ProtocolId = SMB2_TRANSFORM_PROTO_NUM ;
tr_hdr - > OriginalMessageSize = cpu_to_le32 ( orig_len ) ;
tr_hdr - > Flags = cpu_to_le16 ( 0x01 ) ;
2019-06-07 23:16:10 +03:00
if ( cipher_type = = SMB2_ENCRYPTION_AES128_GCM )
get_random_bytes ( & tr_hdr - > Nonce , SMB3_AES128GCM_NONCE ) ;
else
get_random_bytes ( & tr_hdr - > Nonce , SMB3_AES128CCM_NONCE ) ;
2016-11-04 02:47:37 +03:00
memcpy ( & tr_hdr - > SessionId , & shdr - > SessionId , 8 ) ;
}
2018-02-20 04:45:21 +03:00
/* We can not use the normal sg_set_buf() as we will sometimes pass a
* stack object as buf .
*/
static inline void smb2_sg_set_buf ( struct scatterlist * sg , const void * buf ,
unsigned int buflen )
{
sg_set_page ( sg , virt_to_page ( buf ) , buflen , offset_in_page ( buf ) ) ;
}
2018-08-01 02:26:11 +03:00
/* Assumes the first rqst has a transform header as the first iov.
* I . e .
* rqst [ 0 ] . rq_iov [ 0 ] is transform header
* rqst [ 0 ] . rq_iov [ 1 + ] data to be encrypted / decrypted
* rqst [ 1 + ] . rq_iov [ 0 + ] data to be encrypted / decrypted
2018-06-01 03:53:02 +03:00
*/
2016-11-04 02:47:37 +03:00
static struct scatterlist *
2018-08-01 02:26:11 +03:00
init_sg ( int num_rqst , struct smb_rqst * rqst , u8 * sign )
2016-11-04 02:47:37 +03:00
{
2018-08-01 02:26:11 +03:00
unsigned int sg_len ;
2016-11-04 02:47:37 +03:00
struct scatterlist * sg ;
unsigned int i ;
unsigned int j ;
2018-08-01 02:26:11 +03:00
unsigned int idx = 0 ;
int skip ;
sg_len = 1 ;
for ( i = 0 ; i < num_rqst ; i + + )
sg_len + = rqst [ i ] . rq_nvec + rqst [ i ] . rq_npages ;
2016-11-04 02:47:37 +03:00
sg = kmalloc_array ( sg_len , sizeof ( struct scatterlist ) , GFP_KERNEL ) ;
if ( ! sg )
return NULL ;
sg_init_table ( sg , sg_len ) ;
2018-08-01 02:26:11 +03:00
for ( i = 0 ; i < num_rqst ; i + + ) {
for ( j = 0 ; j < rqst [ i ] . rq_nvec ; j + + ) {
/*
* The first rqst has a transform header where the
* first 20 bytes are not part of the encrypted blob
*/
skip = ( i = = 0 ) & & ( j = = 0 ) ? 20 : 0 ;
smb2_sg_set_buf ( & sg [ idx + + ] ,
rqst [ i ] . rq_iov [ j ] . iov_base + skip ,
rqst [ i ] . rq_iov [ j ] . iov_len - skip ) ;
2018-12-31 06:43:40 +03:00
}
2018-08-01 02:26:11 +03:00
for ( j = 0 ; j < rqst [ i ] . rq_npages ; j + + ) {
unsigned int len , offset ;
2018-06-06 01:46:24 +03:00
2018-08-01 02:26:11 +03:00
rqst_page_get_length ( & rqst [ i ] , j , & len , & offset ) ;
sg_set_page ( & sg [ idx + + ] , rqst [ i ] . rq_pages [ j ] , len , offset ) ;
}
2016-11-04 02:47:37 +03:00
}
2018-08-01 02:26:11 +03:00
smb2_sg_set_buf ( & sg [ idx ] , sign , SMB2_SIGNATURE_SIZE ) ;
2016-11-04 02:47:37 +03:00
return sg ;
}
2017-03-01 03:05:19 +03:00
static int
smb2_get_enc_key ( struct TCP_Server_Info * server , __u64 ses_id , int enc , u8 * key )
{
struct cifs_ses * ses ;
u8 * ses_enc_key ;
spin_lock ( & cifs_tcp_ses_lock ) ;
list_for_each_entry ( ses , & server - > smb_ses_list , smb_ses_list ) {
if ( ses - > Suid ! = ses_id )
continue ;
ses_enc_key = enc ? ses - > smb3encryptionkey :
ses - > smb3decryptionkey ;
memcpy ( key , ses_enc_key , SMB3_SIGN_KEY_SIZE ) ;
spin_unlock ( & cifs_tcp_ses_lock ) ;
return 0 ;
}
spin_unlock ( & cifs_tcp_ses_lock ) ;
return 1 ;
}
2016-11-04 02:47:37 +03:00
/*
2018-06-12 01:00:58 +03:00
* Encrypt or decrypt @ rqst message . @ rqst [ 0 ] has the following format :
* iov [ 0 ] - transform header ( associate data ) ,
* iov [ 1 - N ] - SMB2 header and pages - data to encrypt .
* On success return encrypted data in iov [ 1 - N ] and pages , leave iov [ 0 ]
2016-11-04 02:47:37 +03:00
* untouched .
*/
static int
2018-08-01 02:26:11 +03:00
crypt_message ( struct TCP_Server_Info * server , int num_rqst ,
struct smb_rqst * rqst , int enc )
2016-11-04 02:47:37 +03:00
{
struct smb2_transform_hdr * tr_hdr =
2018-08-01 02:26:11 +03:00
( struct smb2_transform_hdr * ) rqst [ 0 ] . rq_iov [ 0 ] . iov_base ;
2018-06-01 03:53:07 +03:00
unsigned int assoc_data_len = sizeof ( struct smb2_transform_hdr ) - 20 ;
2016-11-04 02:47:37 +03:00
int rc = 0 ;
struct scatterlist * sg ;
u8 sign [ SMB2_SIGNATURE_SIZE ] = { } ;
2017-03-01 03:05:19 +03:00
u8 key [ SMB3_SIGN_KEY_SIZE ] ;
2016-11-04 02:47:37 +03:00
struct aead_request * req ;
char * iv ;
unsigned int iv_len ;
2017-10-18 10:00:46 +03:00
DECLARE_CRYPTO_WAIT ( wait ) ;
2016-11-04 02:47:37 +03:00
struct crypto_aead * tfm ;
unsigned int crypt_len = le32_to_cpu ( tr_hdr - > OriginalMessageSize ) ;
2017-03-01 03:05:19 +03:00
rc = smb2_get_enc_key ( server , tr_hdr - > SessionId , enc , key ) ;
if ( rc ) {
cifs_dbg ( VFS , " %s: Could not get %scryption key \n " , __func__ ,
enc ? " en " : " de " ) ;
2016-11-04 02:47:37 +03:00
return 0 ;
}
rc = smb3_crypto_aead_allocate ( server ) ;
if ( rc ) {
cifs_dbg ( VFS , " %s: crypto alloc failed \n " , __func__ ) ;
return rc ;
}
tfm = enc ? server - > secmech . ccmaesencrypt :
server - > secmech . ccmaesdecrypt ;
2017-03-01 03:05:19 +03:00
rc = crypto_aead_setkey ( tfm , key , SMB3_SIGN_KEY_SIZE ) ;
2016-11-04 02:47:37 +03:00
if ( rc ) {
cifs_dbg ( VFS , " %s: Failed to set aead key %d \n " , __func__ , rc ) ;
return rc ;
}
rc = crypto_aead_setauthsize ( tfm , SMB2_SIGNATURE_SIZE ) ;
if ( rc ) {
cifs_dbg ( VFS , " %s: Failed to set authsize %d \n " , __func__ , rc ) ;
return rc ;
}
req = aead_request_alloc ( tfm , GFP_KERNEL ) ;
if ( ! req ) {
2019-05-08 22:36:25 +03:00
cifs_dbg ( VFS , " %s: Failed to alloc aead request \n " , __func__ ) ;
2016-11-04 02:47:37 +03:00
return - ENOMEM ;
}
if ( ! enc ) {
memcpy ( sign , & tr_hdr - > Signature , SMB2_SIGNATURE_SIZE ) ;
crypt_len + = SMB2_SIGNATURE_SIZE ;
}
2018-08-01 02:26:11 +03:00
sg = init_sg ( num_rqst , rqst , sign ) ;
2016-11-04 02:47:37 +03:00
if ( ! sg ) {
2019-05-08 22:36:25 +03:00
cifs_dbg ( VFS , " %s: Failed to init sg \n " , __func__ ) ;
2017-06-11 10:12:47 +03:00
rc = - ENOMEM ;
2016-11-04 02:47:37 +03:00
goto free_req ;
}
iv_len = crypto_aead_ivsize ( tfm ) ;
iv = kzalloc ( iv_len , GFP_KERNEL ) ;
if ( ! iv ) {
2019-05-08 22:36:25 +03:00
cifs_dbg ( VFS , " %s: Failed to alloc iv \n " , __func__ ) ;
2017-06-11 10:12:47 +03:00
rc = - ENOMEM ;
2016-11-04 02:47:37 +03:00
goto free_sg ;
}
2019-06-07 23:16:10 +03:00
if ( server - > cipher_type = = SMB2_ENCRYPTION_AES128_GCM )
memcpy ( iv , ( char * ) tr_hdr - > Nonce , SMB3_AES128GCM_NONCE ) ;
else {
iv [ 0 ] = 3 ;
memcpy ( iv + 1 , ( char * ) tr_hdr - > Nonce , SMB3_AES128CCM_NONCE ) ;
}
2016-11-04 02:47:37 +03:00
aead_request_set_crypt ( req , sg , sg , crypt_len , iv ) ;
aead_request_set_ad ( req , assoc_data_len ) ;
aead_request_set_callback ( req , CRYPTO_TFM_REQ_MAY_BACKLOG ,
2017-10-18 10:00:46 +03:00
crypto_req_done , & wait ) ;
2016-11-04 02:47:37 +03:00
2017-10-18 10:00:46 +03:00
rc = crypto_wait_req ( enc ? crypto_aead_encrypt ( req )
: crypto_aead_decrypt ( req ) , & wait ) ;
2016-11-04 02:47:37 +03:00
if ( ! rc & & enc )
memcpy ( & tr_hdr - > Signature , sign , SMB2_SIGNATURE_SIZE ) ;
kfree ( iv ) ;
free_sg :
kfree ( sg ) ;
free_req :
kfree ( req ) ;
return rc ;
}
2018-08-01 02:26:11 +03:00
void
smb3_free_compound_rqst ( int num_rqst , struct smb_rqst * rqst )
{
int i , j ;
for ( i = 0 ; i < num_rqst ; i + + ) {
if ( rqst [ i ] . rq_pages ) {
for ( j = rqst [ i ] . rq_npages - 1 ; j > = 0 ; j - - )
put_page ( rqst [ i ] . rq_pages [ j ] ) ;
kfree ( rqst [ i ] . rq_pages ) ;
}
}
}
/*
* This function will initialize new_rq and encrypt the content .
* The first entry , new_rq [ 0 ] , only contains a single iov which contains
* a smb2_transform_hdr and is pre - allocated by the caller .
* This function then populates new_rq [ 1 + ] with the content from olq_rq [ 0 + ] .
*
* The end result is an array of smb_rqst structures where the first structure
* only contains a single iov for the transform header which we then can pass
* to crypt_message ( ) .
*
* new_rq [ 0 ] . rq_iov [ 0 ] : smb2_transform_hdr pre - allocated by the caller
* new_rq [ 1 + ] . rq_iov [ * ] = = old_rq [ 0 + ] . rq_iov [ * ] : SMB2 / 3 requests
*/
2016-11-04 02:47:37 +03:00
static int
2018-08-01 02:26:11 +03:00
smb3_init_transform_rq ( struct TCP_Server_Info * server , int num_rqst ,
struct smb_rqst * new_rq , struct smb_rqst * old_rq )
2016-11-04 02:47:37 +03:00
{
struct page * * pages ;
2018-08-01 02:26:11 +03:00
struct smb2_transform_hdr * tr_hdr = new_rq [ 0 ] . rq_iov [ 0 ] . iov_base ;
unsigned int npages ;
unsigned int orig_len = 0 ;
int i , j ;
2016-11-04 02:47:37 +03:00
int rc = - ENOMEM ;
2018-08-01 02:26:11 +03:00
for ( i = 1 ; i < num_rqst ; i + + ) {
npages = old_rq [ i - 1 ] . rq_npages ;
pages = kmalloc_array ( npages , sizeof ( struct page * ) ,
GFP_KERNEL ) ;
if ( ! pages )
goto err_free ;
new_rq [ i ] . rq_pages = pages ;
new_rq [ i ] . rq_npages = npages ;
new_rq [ i ] . rq_offset = old_rq [ i - 1 ] . rq_offset ;
new_rq [ i ] . rq_pagesz = old_rq [ i - 1 ] . rq_pagesz ;
new_rq [ i ] . rq_tailsz = old_rq [ i - 1 ] . rq_tailsz ;
new_rq [ i ] . rq_iov = old_rq [ i - 1 ] . rq_iov ;
new_rq [ i ] . rq_nvec = old_rq [ i - 1 ] . rq_nvec ;
orig_len + = smb_rqst_len ( server , & old_rq [ i - 1 ] ) ;
for ( j = 0 ; j < npages ; j + + ) {
pages [ j ] = alloc_page ( GFP_KERNEL | __GFP_HIGHMEM ) ;
if ( ! pages [ j ] )
goto err_free ;
}
2016-11-04 02:47:37 +03:00
2018-08-01 02:26:11 +03:00
/* copy pages form the old */
for ( j = 0 ; j < npages ; j + + ) {
char * dst , * src ;
unsigned int offset , len ;
2018-06-01 03:53:02 +03:00
2018-08-01 02:26:11 +03:00
rqst_page_get_length ( & new_rq [ i ] , j , & len , & offset ) ;
2016-11-04 02:47:37 +03:00
2018-08-01 02:26:11 +03:00
dst = ( char * ) kmap ( new_rq [ i ] . rq_pages [ j ] ) + offset ;
src = ( char * ) kmap ( old_rq [ i - 1 ] . rq_pages [ j ] ) + offset ;
2016-11-04 02:47:37 +03:00
2018-08-01 02:26:11 +03:00
memcpy ( dst , src , len ) ;
kunmap ( new_rq [ i ] . rq_pages [ j ] ) ;
kunmap ( old_rq [ i - 1 ] . rq_pages [ j ] ) ;
}
}
2018-06-15 16:22:44 +03:00
2018-08-01 02:26:11 +03:00
/* fill the 1st iov with a transform header */
2019-06-07 23:16:10 +03:00
fill_transform_hdr ( tr_hdr , orig_len , old_rq , server - > cipher_type ) ;
2018-06-06 01:46:24 +03:00
2018-08-01 02:26:11 +03:00
rc = crypt_message ( server , num_rqst , new_rq , 1 ) ;
2019-05-08 22:36:25 +03:00
cifs_dbg ( FYI , " Encrypt message returned %d \n " , rc ) ;
2016-11-04 02:47:37 +03:00
if ( rc )
2018-08-01 02:26:11 +03:00
goto err_free ;
2016-11-04 02:47:37 +03:00
return rc ;
2018-08-01 02:26:11 +03:00
err_free :
smb3_free_compound_rqst ( num_rqst - 1 , & new_rq [ 1 ] ) ;
2016-11-04 02:47:37 +03:00
return rc ;
}
2016-11-18 02:24:46 +03:00
static int
smb3_is_transform_hdr ( void * buf )
{
struct smb2_transform_hdr * trhdr = buf ;
return trhdr - > ProtocolId = = SMB2_TRANSFORM_PROTO_NUM ;
}
static int
decrypt_raw_data ( struct TCP_Server_Info * server , char * buf ,
unsigned int buf_data_size , struct page * * pages ,
unsigned int npages , unsigned int page_data_size )
{
2018-06-12 01:00:58 +03:00
struct kvec iov [ 2 ] ;
2016-11-18 02:24:46 +03:00
struct smb_rqst rqst = { NULL } ;
int rc ;
2018-06-12 01:00:58 +03:00
iov [ 0 ] . iov_base = buf ;
iov [ 0 ] . iov_len = sizeof ( struct smb2_transform_hdr ) ;
iov [ 1 ] . iov_base = buf + sizeof ( struct smb2_transform_hdr ) ;
iov [ 1 ] . iov_len = buf_data_size ;
2016-11-18 02:24:46 +03:00
rqst . rq_iov = iov ;
2018-06-12 01:00:58 +03:00
rqst . rq_nvec = 2 ;
2016-11-18 02:24:46 +03:00
rqst . rq_pages = pages ;
rqst . rq_npages = npages ;
rqst . rq_pagesz = PAGE_SIZE ;
rqst . rq_tailsz = ( page_data_size % PAGE_SIZE ) ? : PAGE_SIZE ;
2018-08-01 02:26:11 +03:00
rc = crypt_message ( server , 1 , & rqst , 0 ) ;
2019-05-08 22:36:25 +03:00
cifs_dbg ( FYI , " Decrypt message returned %d \n " , rc ) ;
2016-11-18 02:24:46 +03:00
if ( rc )
return rc ;
2018-06-12 01:00:58 +03:00
memmove ( buf , iov [ 1 ] . iov_base , buf_data_size ) ;
2018-06-01 03:53:02 +03:00
server - > total_read = buf_data_size + page_data_size ;
2016-11-18 02:24:46 +03:00
return rc ;
}
2016-11-18 03:20:23 +03:00
static int
read_data_into_pages ( struct TCP_Server_Info * server , struct page * * pages ,
unsigned int npages , unsigned int len )
{
int i ;
int length ;
for ( i = 0 ; i < npages ; i + + ) {
struct page * page = pages [ i ] ;
size_t n ;
n = len ;
if ( len > = PAGE_SIZE ) {
/* enough data to fill the page */
n = PAGE_SIZE ;
len - = n ;
} else {
zero_user ( page , len , PAGE_SIZE - len ) ;
len = 0 ;
}
2018-05-30 22:47:55 +03:00
length = cifs_read_page_from_socket ( server , page , 0 , n ) ;
2016-11-18 03:20:23 +03:00
if ( length < 0 )
return length ;
server - > total_read + = length ;
}
return 0 ;
}
static int
init_read_bvec ( struct page * * pages , unsigned int npages , unsigned int data_size ,
unsigned int cur_off , struct bio_vec * * page_vec )
{
struct bio_vec * bvec ;
int i ;
bvec = kcalloc ( npages , sizeof ( struct bio_vec ) , GFP_KERNEL ) ;
if ( ! bvec )
return - ENOMEM ;
for ( i = 0 ; i < npages ; i + + ) {
bvec [ i ] . bv_page = pages [ i ] ;
bvec [ i ] . bv_offset = ( i = = 0 ) ? cur_off : 0 ;
bvec [ i ] . bv_len = min_t ( unsigned int , PAGE_SIZE , data_size ) ;
data_size - = bvec [ i ] . bv_len ;
}
if ( data_size ! = 0 ) {
cifs_dbg ( VFS , " %s: something went wrong \n " , __func__ ) ;
kfree ( bvec ) ;
return - EIO ;
}
* page_vec = bvec ;
return 0 ;
}
2016-11-18 02:24:46 +03:00
static int
handle_read_data ( struct TCP_Server_Info * server , struct mid_q_entry * mid ,
char * buf , unsigned int buf_len , struct page * * pages ,
unsigned int npages , unsigned int page_data_size )
{
unsigned int data_offset ;
unsigned int data_len ;
2016-11-18 03:20:23 +03:00
unsigned int cur_off ;
unsigned int cur_page_idx ;
unsigned int pad_len ;
2016-11-18 02:24:46 +03:00
struct cifs_readdata * rdata = mid - > callback_data ;
2018-06-01 03:53:06 +03:00
struct smb2_sync_hdr * shdr = ( struct smb2_sync_hdr * ) buf ;
2016-11-18 02:24:46 +03:00
struct bio_vec * bvec = NULL ;
struct iov_iter iter ;
struct kvec iov ;
int length ;
2017-11-23 03:38:46 +03:00
bool use_rdma_mr = false ;
2016-11-18 02:24:46 +03:00
if ( shdr - > Command ! = SMB2_READ ) {
cifs_dbg ( VFS , " only big read responses are supported \n " ) ;
return - ENOTSUPP ;
}
2017-07-09 00:32:00 +03:00
if ( server - > ops - > is_session_expired & &
server - > ops - > is_session_expired ( buf ) ) {
cifs_reconnect ( server ) ;
wake_up ( & server - > response_q ) ;
return - 1 ;
}
2016-11-18 02:24:46 +03:00
if ( server - > ops - > is_status_pending & &
2019-01-24 04:11:16 +03:00
server - > ops - > is_status_pending ( buf , server ) )
2016-11-18 02:24:46 +03:00
return - 1 ;
2019-01-19 02:38:11 +03:00
/* set up first two iov to get credits */
rdata - > iov [ 0 ] . iov_base = buf ;
2019-01-18 03:18:38 +03:00
rdata - > iov [ 0 ] . iov_len = 0 ;
rdata - > iov [ 1 ] . iov_base = buf ;
2019-01-19 02:38:11 +03:00
rdata - > iov [ 1 ] . iov_len =
2019-01-18 03:18:38 +03:00
min_t ( unsigned int , buf_len , server - > vals - > read_rsp_size ) ;
2019-01-19 02:38:11 +03:00
cifs_dbg ( FYI , " 0: iov_base=%p iov_len=%zu \n " ,
rdata - > iov [ 0 ] . iov_base , rdata - > iov [ 0 ] . iov_len ) ;
cifs_dbg ( FYI , " 1: iov_base=%p iov_len=%zu \n " ,
rdata - > iov [ 1 ] . iov_base , rdata - > iov [ 1 ] . iov_len ) ;
rdata - > result = server - > ops - > map_error ( buf , true ) ;
2016-11-18 02:24:46 +03:00
if ( rdata - > result ! = 0 ) {
cifs_dbg ( FYI , " %s: server returned error %d \n " ,
__func__ , rdata - > result ) ;
2019-01-19 02:38:11 +03:00
/* normal error on read response */
dequeue_mid ( mid , false ) ;
2016-11-18 02:24:46 +03:00
return 0 ;
}
2018-06-01 03:53:07 +03:00
data_offset = server - > ops - > read_data_offset ( buf ) ;
2017-11-23 03:38:46 +03:00
# ifdef CONFIG_CIFS_SMB_DIRECT
use_rdma_mr = rdata - > mr ;
# endif
data_len = server - > ops - > read_data_length ( buf , use_rdma_mr ) ;
2016-11-18 02:24:46 +03:00
if ( data_offset < server - > vals - > read_rsp_size ) {
/*
* win2k8 sometimes sends an offset of 0 when the read
* is beyond the EOF . Treat it as if the data starts just after
* the header .
*/
cifs_dbg ( FYI , " %s: data offset (%u) inside read response header \n " ,
__func__ , data_offset ) ;
data_offset = server - > vals - > read_rsp_size ;
} else if ( data_offset > MAX_CIFS_SMALL_BUFFER_SIZE ) {
/* data_offset is beyond the end of smallbuf */
cifs_dbg ( FYI , " %s: data offset (%u) beyond end of smallbuf \n " ,
__func__ , data_offset ) ;
rdata - > result = - EIO ;
dequeue_mid ( mid , rdata - > result ) ;
return 0 ;
}
2016-11-18 03:20:23 +03:00
pad_len = data_offset - server - > vals - > read_rsp_size ;
2016-11-18 02:24:46 +03:00
if ( buf_len < = data_offset ) {
/* read response payload is in pages */
2016-11-18 03:20:23 +03:00
cur_page_idx = pad_len / PAGE_SIZE ;
cur_off = pad_len % PAGE_SIZE ;
if ( cur_page_idx ! = 0 ) {
/* data offset is beyond the 1st page of response */
cifs_dbg ( FYI , " %s: data offset (%u) beyond 1st page of response \n " ,
__func__ , data_offset ) ;
rdata - > result = - EIO ;
dequeue_mid ( mid , rdata - > result ) ;
return 0 ;
}
if ( data_len > page_data_size - pad_len ) {
/* data_len is corrupt -- discard frame */
rdata - > result = - EIO ;
dequeue_mid ( mid , rdata - > result ) ;
return 0 ;
}
rdata - > result = init_read_bvec ( pages , npages , page_data_size ,
cur_off , & bvec ) ;
if ( rdata - > result ! = 0 ) {
dequeue_mid ( mid , rdata - > result ) ;
return 0 ;
}
2018-10-20 02:57:56 +03:00
iov_iter_bvec ( & iter , WRITE , bvec , npages , data_len ) ;
2016-11-18 02:24:46 +03:00
} else if ( buf_len > = data_offset + data_len ) {
/* read response payload is in buf */
WARN_ONCE ( npages > 0 , " read data can be either in buf or in pages " ) ;
iov . iov_base = buf + data_offset ;
iov . iov_len = data_len ;
2018-10-20 02:57:56 +03:00
iov_iter_kvec ( & iter , WRITE , & iov , 1 , data_len ) ;
2016-11-18 02:24:46 +03:00
} else {
/* read response payload cannot be in both buf and pages */
WARN_ONCE ( 1 , " buf can not contain only a part of read data " ) ;
rdata - > result = - EIO ;
dequeue_mid ( mid , rdata - > result ) ;
return 0 ;
}
length = rdata - > copy_into_pages ( server , rdata , & iter ) ;
kfree ( bvec ) ;
if ( length < 0 )
return length ;
dequeue_mid ( mid , false ) ;
return length ;
}
2016-11-18 03:20:23 +03:00
static int
receive_encrypted_read ( struct TCP_Server_Info * server , struct mid_q_entry * * mid )
{
char * buf = server - > smallbuf ;
struct smb2_transform_hdr * tr_hdr = ( struct smb2_transform_hdr * ) buf ;
unsigned int npages ;
struct page * * pages ;
unsigned int len ;
2018-06-01 03:53:07 +03:00
unsigned int buflen = server - > pdu_size ;
2016-11-18 03:20:23 +03:00
int rc ;
int i = 0 ;
2018-06-01 03:53:07 +03:00
len = min_t ( unsigned int , buflen , server - > vals - > read_rsp_size +
2016-11-18 03:20:23 +03:00
sizeof ( struct smb2_transform_hdr ) ) - HEADER_SIZE ( server ) + 1 ;
rc = cifs_read_from_socket ( server , buf + HEADER_SIZE ( server ) - 1 , len ) ;
if ( rc < 0 )
return rc ;
server - > total_read + = rc ;
2018-06-01 03:53:07 +03:00
len = le32_to_cpu ( tr_hdr - > OriginalMessageSize ) -
2018-03-31 03:45:31 +03:00
server - > vals - > read_rsp_size ;
2016-11-18 03:20:23 +03:00
npages = DIV_ROUND_UP ( len , PAGE_SIZE ) ;
pages = kmalloc_array ( npages , sizeof ( struct page * ) , GFP_KERNEL ) ;
if ( ! pages ) {
rc = - ENOMEM ;
goto discard_data ;
}
for ( ; i < npages ; i + + ) {
pages [ i ] = alloc_page ( GFP_KERNEL | __GFP_HIGHMEM ) ;
if ( ! pages [ i ] ) {
rc = - ENOMEM ;
goto discard_data ;
}
}
/* read read data into pages */
rc = read_data_into_pages ( server , pages , npages , len ) ;
if ( rc )
goto free_pages ;
2017-04-10 20:31:33 +03:00
rc = cifs_discard_remaining_data ( server ) ;
2016-11-18 03:20:23 +03:00
if ( rc )
goto free_pages ;
2018-06-01 03:53:07 +03:00
rc = decrypt_raw_data ( server , buf , server - > vals - > read_rsp_size ,
2016-11-18 03:20:23 +03:00
pages , npages , len ) ;
if ( rc )
goto free_pages ;
* mid = smb2_find_mid ( server , buf ) ;
if ( * mid = = NULL )
cifs_dbg ( FYI , " mid not found \n " ) ;
else {
cifs_dbg ( FYI , " mid found \n " ) ;
( * mid ) - > decrypted = true ;
rc = handle_read_data ( server , * mid , buf ,
server - > vals - > read_rsp_size ,
pages , npages , len ) ;
}
free_pages :
for ( i = i - 1 ; i > = 0 ; i - - )
put_page ( pages [ i ] ) ;
kfree ( pages ) ;
return rc ;
discard_data :
2017-04-10 20:31:33 +03:00
cifs_discard_remaining_data ( server ) ;
2016-11-18 03:20:23 +03:00
goto free_pages ;
}
2016-11-18 02:24:46 +03:00
static int
receive_encrypted_standard ( struct TCP_Server_Info * server ,
2018-08-08 08:07:45 +03:00
struct mid_q_entry * * mids , char * * bufs ,
int * num_mids )
2016-11-18 02:24:46 +03:00
{
2018-08-08 08:07:45 +03:00
int ret , length ;
2016-11-18 02:24:46 +03:00
char * buf = server - > smallbuf ;
2018-08-08 08:07:45 +03:00
char * tmpbuf ;
struct smb2_sync_hdr * shdr ;
2018-04-09 11:06:26 +03:00
unsigned int pdu_length = server - > pdu_size ;
2016-11-18 02:24:46 +03:00
unsigned int buf_size ;
struct mid_q_entry * mid_entry ;
2018-08-08 08:07:45 +03:00
int next_is_large ;
char * next_buffer = NULL ;
* num_mids = 0 ;
2016-11-18 02:24:46 +03:00
/* switch to large buffer if too big for a small one */
2018-06-01 03:53:07 +03:00
if ( pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE ) {
2016-11-18 02:24:46 +03:00
server - > large_buf = true ;
memcpy ( server - > bigbuf , buf , server - > total_read ) ;
buf = server - > bigbuf ;
}
/* now read the rest */
length = cifs_read_from_socket ( server , buf + HEADER_SIZE ( server ) - 1 ,
2018-06-01 03:53:07 +03:00
pdu_length - HEADER_SIZE ( server ) + 1 ) ;
2016-11-18 02:24:46 +03:00
if ( length < 0 )
return length ;
server - > total_read + = length ;
2018-06-01 03:53:07 +03:00
buf_size = pdu_length - sizeof ( struct smb2_transform_hdr ) ;
2016-11-18 02:24:46 +03:00
length = decrypt_raw_data ( server , buf , buf_size , NULL , 0 , 0 ) ;
if ( length )
return length ;
2018-08-08 08:07:45 +03:00
next_is_large = server - > large_buf ;
one_more :
shdr = ( struct smb2_sync_hdr * ) buf ;
if ( shdr - > NextCommand ) {
if ( next_is_large ) {
tmpbuf = server - > bigbuf ;
next_buffer = ( char * ) cifs_buf_get ( ) ;
} else {
tmpbuf = server - > smallbuf ;
next_buffer = ( char * ) cifs_small_buf_get ( ) ;
}
memcpy ( next_buffer ,
tmpbuf + le32_to_cpu ( shdr - > NextCommand ) ,
pdu_length - le32_to_cpu ( shdr - > NextCommand ) ) ;
}
2016-11-18 02:24:46 +03:00
mid_entry = smb2_find_mid ( server , buf ) ;
if ( mid_entry = = NULL )
cifs_dbg ( FYI , " mid not found \n " ) ;
else {
cifs_dbg ( FYI , " mid found \n " ) ;
mid_entry - > decrypted = true ;
2018-08-08 08:07:45 +03:00
mid_entry - > resp_buf_size = server - > pdu_size ;
2016-11-18 02:24:46 +03:00
}
2018-08-08 08:07:45 +03:00
if ( * num_mids > = MAX_COMPOUND ) {
cifs_dbg ( VFS , " too many PDUs in compound \n " ) ;
return - 1 ;
}
bufs [ * num_mids ] = buf ;
mids [ ( * num_mids ) + + ] = mid_entry ;
2016-11-18 02:24:46 +03:00
if ( mid_entry & & mid_entry - > handle )
2018-08-08 08:07:45 +03:00
ret = mid_entry - > handle ( server , mid_entry ) ;
else
ret = cifs_handle_standard ( server , mid_entry ) ;
if ( ret = = 0 & & shdr - > NextCommand ) {
pdu_length - = le32_to_cpu ( shdr - > NextCommand ) ;
server - > large_buf = next_is_large ;
if ( next_is_large )
server - > bigbuf = next_buffer ;
else
server - > smallbuf = next_buffer ;
buf + = le32_to_cpu ( shdr - > NextCommand ) ;
goto one_more ;
}
2016-11-18 02:24:46 +03:00
2018-08-08 08:07:45 +03:00
return ret ;
2016-11-18 02:24:46 +03:00
}
static int
2018-08-08 08:07:45 +03:00
smb3_receive_transform ( struct TCP_Server_Info * server ,
struct mid_q_entry * * mids , char * * bufs , int * num_mids )
2016-11-18 02:24:46 +03:00
{
char * buf = server - > smallbuf ;
2018-04-09 11:06:26 +03:00
unsigned int pdu_length = server - > pdu_size ;
2016-11-18 02:24:46 +03:00
struct smb2_transform_hdr * tr_hdr = ( struct smb2_transform_hdr * ) buf ;
unsigned int orig_len = le32_to_cpu ( tr_hdr - > OriginalMessageSize ) ;
2018-06-01 03:53:07 +03:00
if ( pdu_length < sizeof ( struct smb2_transform_hdr ) +
2016-11-18 02:24:46 +03:00
sizeof ( struct smb2_sync_hdr ) ) {
cifs_dbg ( VFS , " Transform message is too small (%u) \n " ,
pdu_length ) ;
cifs_reconnect ( server ) ;
wake_up ( & server - > response_q ) ;
return - ECONNABORTED ;
}
2018-06-01 03:53:07 +03:00
if ( pdu_length < orig_len + sizeof ( struct smb2_transform_hdr ) ) {
2016-11-18 02:24:46 +03:00
cifs_dbg ( VFS , " Transform message is broken \n " ) ;
cifs_reconnect ( server ) ;
wake_up ( & server - > response_q ) ;
return - ECONNABORTED ;
}
2018-08-08 08:07:45 +03:00
/* TODO: add support for compounds containing READ. */
2019-01-01 01:13:34 +03:00
if ( pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE ( server ) ) {
* num_mids = 1 ;
2018-08-08 08:07:45 +03:00
return receive_encrypted_read ( server , & mids [ 0 ] ) ;
2019-01-01 01:13:34 +03:00
}
2016-11-18 02:24:46 +03:00
2018-08-08 08:07:45 +03:00
return receive_encrypted_standard ( server , mids , bufs , num_mids ) ;
2016-11-18 02:24:46 +03:00
}
int
smb3_handle_read_data ( struct TCP_Server_Info * server , struct mid_q_entry * mid )
{
char * buf = server - > large_buf ? server - > bigbuf : server - > smallbuf ;
2018-06-01 03:53:07 +03:00
return handle_read_data ( server , mid , buf , server - > pdu_size ,
2016-11-18 02:24:46 +03:00
NULL , 0 , 0 ) ;
}
2018-06-01 03:53:08 +03:00
static int
smb2_next_header ( char * buf )
{
struct smb2_sync_hdr * hdr = ( struct smb2_sync_hdr * ) buf ;
struct smb2_transform_hdr * t_hdr = ( struct smb2_transform_hdr * ) buf ;
if ( hdr - > ProtocolId = = SMB2_TRANSFORM_PROTO_NUM )
return sizeof ( struct smb2_transform_hdr ) +
le32_to_cpu ( t_hdr - > OriginalMessageSize ) ;
return le32_to_cpu ( hdr - > NextCommand ) ;
}
2019-03-14 08:29:17 +03:00
static int
smb2_make_node ( unsigned int xid , struct inode * inode ,
struct dentry * dentry , struct cifs_tcon * tcon ,
char * full_path , umode_t mode , dev_t dev )
{
struct cifs_sb_info * cifs_sb = CIFS_SB ( inode - > i_sb ) ;
int rc = - EPERM ;
int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL ;
FILE_ALL_INFO * buf = NULL ;
struct cifs_io_parms io_parms ;
__u32 oplock = 0 ;
struct cifs_fid fid ;
struct cifs_open_parms oparms ;
unsigned int bytes_written ;
struct win_dev * pdev ;
struct kvec iov [ 2 ] ;
/*
* Check if mounted with mount parm ' sfu ' mount parm .
* SFU emulation should work with all servers , but only
* supports block and char device ( no socket & fifo ) ,
* and was used by default in earlier versions of Windows
*/
if ( ! ( cifs_sb - > mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL ) )
goto out ;
/*
* TODO : Add ability to create instead via reparse point . Windows ( e . g .
* their current NFS server ) uses this approach to expose special files
* over SMB2 / SMB3 and Samba will do this with SMB3 .1 .1 POSIX Extensions
*/
if ( ! S_ISCHR ( mode ) & & ! S_ISBLK ( mode ) )
goto out ;
cifs_dbg ( FYI , " sfu compat create special file \n " ) ;
buf = kmalloc ( sizeof ( FILE_ALL_INFO ) , GFP_KERNEL ) ;
if ( buf = = NULL ) {
rc = - ENOMEM ;
goto out ;
}
if ( backup_cred ( cifs_sb ) )
create_options | = CREATE_OPEN_BACKUP_INTENT ;
oparms . tcon = tcon ;
oparms . cifs_sb = cifs_sb ;
oparms . desired_access = GENERIC_WRITE ;
oparms . create_options = create_options ;
oparms . disposition = FILE_CREATE ;
oparms . path = full_path ;
oparms . fid = & fid ;
oparms . reconnect = false ;
if ( tcon - > ses - > server - > oplocks )
oplock = REQ_OPLOCK ;
else
oplock = 0 ;
rc = tcon - > ses - > server - > ops - > open ( xid , & oparms , & oplock , buf ) ;
if ( rc )
goto out ;
/*
* BB Do not bother to decode buf since no local inode yet to put
* timestamps in , but we can reuse it safely .
*/
pdev = ( struct win_dev * ) buf ;
io_parms . pid = current - > tgid ;
io_parms . tcon = tcon ;
io_parms . offset = 0 ;
io_parms . length = sizeof ( struct win_dev ) ;
iov [ 1 ] . iov_base = buf ;
iov [ 1 ] . iov_len = sizeof ( struct win_dev ) ;
if ( S_ISCHR ( mode ) ) {
memcpy ( pdev - > type , " IntxCHR " , 8 ) ;
pdev - > major = cpu_to_le64 ( MAJOR ( dev ) ) ;
pdev - > minor = cpu_to_le64 ( MINOR ( dev ) ) ;
rc = tcon - > ses - > server - > ops - > sync_write ( xid , & fid , & io_parms ,
& bytes_written , iov , 1 ) ;
} else if ( S_ISBLK ( mode ) ) {
memcpy ( pdev - > type , " IntxBLK " , 8 ) ;
pdev - > major = cpu_to_le64 ( MAJOR ( dev ) ) ;
pdev - > minor = cpu_to_le64 ( MINOR ( dev ) ) ;
rc = tcon - > ses - > server - > ops - > sync_write ( xid , & fid , & io_parms ,
& bytes_written , iov , 1 ) ;
}
tcon - > ses - > server - > ops - > close ( xid , tcon , & fid ) ;
d_drop ( dentry ) ;
/* FIXME: add code here to set EAs */
out :
kfree ( buf ) ;
return rc ;
}
2013-09-05 16:11:28 +04:00
struct smb_version_operations smb20_operations = {
2012-09-19 17:22:43 +04:00
. compare_fids = smb2_compare_fids ,
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 ,
2014-06-05 19:03:27 +04:00
. wait_mtu_credits = cifs_wait_mtu_credits ,
2011-12-26 22:53:34 +04:00
. get_next_mid = smb2_get_next_mid ,
2019-03-05 01:02:50 +03:00
. revert_current_mid = smb2_revert_current_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 ,
2012-09-19 03:20:33 +04:00
. is_oplock_break = smb2_is_valid_oplock_break ,
2017-03-04 02:41:38 +03:00
. handle_cancelled_mid = smb2_handle_cancelled_mid ,
2014-03-11 20:11:47 +04:00
. downgrade_oplock = smb2_downgrade_oplock ,
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 ,
2013-10-09 11:07:00 +04:00
. qfs_tcon = smb2_qfs_tcon ,
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-09-19 03:20:32 +04:00
. set_path_size = smb2_set_path_size ,
. set_file_size = smb2_set_file_size ,
2012-09-19 03:20:32 +04:00
. set_file_info = smb2_set_file_info ,
2013-10-15 00:31:32 +04:00
. set_compression = smb2_set_compression ,
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 ,
2013-08-14 19:25:21 +04:00
. query_symlink = smb2_query_symlink ,
2016-07-11 18:53:20 +03:00
. query_mf_symlink = smb3_query_mf_symlink ,
. create_mf_symlink = smb3_create_mf_symlink ,
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 ,
2012-09-19 03:20:33 +04:00
. query_dir_first = smb2_query_dir_first ,
. query_dir_next = smb2_query_dir_next ,
. close_dir = smb2_close_dir ,
. calc_smb_size = smb2_calc_size ,
2012-09-19 03:20:33 +04:00
. is_status_pending = smb2_is_status_pending ,
2017-07-09 00:32:00 +03:00
. is_session_expired = smb2_is_session_expired ,
2012-09-19 03:20:33 +04:00
. oplock_response = smb2_oplock_response ,
2012-09-19 03:20:34 +04:00
. queryfs = smb2_queryfs ,
2012-09-19 17:22:43 +04:00
. mand_lock = smb2_mand_lock ,
. mand_unlock_range = smb2_unlock_range ,
2012-09-19 17:22:44 +04:00
. push_mand_locks = smb2_push_mandatory_locks ,
2012-09-19 17:22:44 +04:00
. get_lease_key = smb2_get_lease_key ,
. set_lease_key = smb2_set_lease_key ,
. new_lease_key = smb2_new_lease_key ,
2012-12-09 08:08:06 +04:00
. calc_signature = smb2_calc_signature ,
2013-09-05 16:11:28 +04:00
. is_read_op = smb2_is_read_op ,
. set_oplock_level = smb2_set_oplock_level ,
2013-09-04 13:07:41 +04:00
. create_lease_buf = smb2_create_lease_buf ,
2013-09-05 20:16:45 +04:00
. parse_lease_buf = smb2_parse_lease_buf ,
2017-04-04 10:12:04 +03:00
. copychunk_range = smb2_copychunk_range ,
2014-06-22 11:03:22 +04:00
. wp_retry_size = smb2_wp_retry_size ,
2014-08-18 20:49:57 +04:00
. dir_needs_close = smb2_dir_needs_close ,
2017-02-13 18:16:49 +03:00
. get_dfs_refer = smb2_get_dfs_refer ,
2017-01-18 13:05:57 +03:00
. select_sectype = smb2_select_sectype ,
2017-08-24 04:24:55 +03:00
# ifdef CONFIG_CIFS_XATTR
. query_all_EAs = smb2_query_eas ,
2017-08-24 04:24:56 +03:00
. set_EA = smb2_set_ea ,
2017-08-24 04:24:55 +03:00
# endif /* CIFS_XATTR */
2017-06-23 06:52:05 +03:00
. get_acl = get_smb2_acl ,
. get_acl_by_fid = get_smb2_acl_by_fid ,
2017-06-29 06:37:32 +03:00
. set_acl = set_smb2_acl ,
2018-06-01 03:53:08 +03:00
. next_header = smb2_next_header ,
2018-10-08 03:19:58 +03:00
. ioctl_query_info = smb2_ioctl_query_info ,
2019-03-14 08:29:17 +03:00
. make_node = smb2_make_node ,
2019-04-25 09:45:29 +03:00
. fiemap = smb3_fiemap ,
2019-05-15 00:17:02 +03:00
. llseek = smb3_llseek ,
2012-12-09 08:08:06 +04:00
} ;
2013-09-05 16:11:28 +04:00
struct smb_version_operations smb21_operations = {
. compare_fids = smb2_compare_fids ,
. setup_request = smb2_setup_request ,
. setup_async_request = smb2_setup_async_request ,
. check_receive = smb2_check_receive ,
. add_credits = smb2_add_credits ,
. set_credits = smb2_set_credits ,
. get_credits_field = smb2_get_credits_field ,
. get_credits = smb2_get_credits ,
2014-06-05 19:03:27 +04:00
. wait_mtu_credits = smb2_wait_mtu_credits ,
2019-01-24 05:15:52 +03:00
. adjust_credits = smb2_adjust_credits ,
2013-09-05 16:11:28 +04:00
. get_next_mid = smb2_get_next_mid ,
2019-03-05 01:02:50 +03:00
. revert_current_mid = smb2_revert_current_mid ,
2013-09-05 16:11:28 +04:00
. read_data_offset = smb2_read_data_offset ,
. read_data_length = smb2_read_data_length ,
. map_error = map_smb2_to_linux_error ,
. find_mid = smb2_find_mid ,
. check_message = smb2_check_message ,
. dump_detail = smb2_dump_detail ,
. clear_stats = smb2_clear_stats ,
. print_stats = smb2_print_stats ,
. is_oplock_break = smb2_is_valid_oplock_break ,
2017-03-04 02:41:38 +03:00
. handle_cancelled_mid = smb2_handle_cancelled_mid ,
2019-02-14 02:43:08 +03:00
. downgrade_oplock = smb21_downgrade_oplock ,
2013-09-05 16:11:28 +04:00
. need_neg = smb2_need_neg ,
. negotiate = smb2_negotiate ,
. negotiate_wsize = smb2_negotiate_wsize ,
. negotiate_rsize = smb2_negotiate_rsize ,
. sess_setup = SMB2_sess_setup ,
. logoff = SMB2_logoff ,
. tree_connect = SMB2_tcon ,
. tree_disconnect = SMB2_tdis ,
2013-10-09 11:07:00 +04:00
. qfs_tcon = smb2_qfs_tcon ,
2013-09-05 16:11:28 +04:00
. is_path_accessible = smb2_is_path_accessible ,
. can_echo = smb2_can_echo ,
. echo = SMB2_echo ,
. query_path_info = smb2_query_path_info ,
. get_srv_inum = smb2_get_srv_inum ,
. query_file_info = smb2_query_file_info ,
. set_path_size = smb2_set_path_size ,
. set_file_size = smb2_set_file_size ,
. set_file_info = smb2_set_file_info ,
2013-10-15 00:31:32 +04:00
. set_compression = smb2_set_compression ,
2013-09-05 16:11:28 +04:00
. mkdir = smb2_mkdir ,
. mkdir_setinfo = smb2_mkdir_setinfo ,
. rmdir = smb2_rmdir ,
. unlink = smb2_unlink ,
. rename = smb2_rename_path ,
. create_hardlink = smb2_create_hardlink ,
. query_symlink = smb2_query_symlink ,
2014-09-16 16:18:19 +04:00
. query_mf_symlink = smb3_query_mf_symlink ,
2014-09-15 13:49:28 +04:00
. create_mf_symlink = smb3_create_mf_symlink ,
2013-09-05 16:11:28 +04:00
. open = smb2_open_file ,
. set_fid = smb2_set_fid ,
. close = smb2_close_file ,
. flush = smb2_flush_file ,
. async_readv = smb2_async_readv ,
. async_writev = smb2_async_writev ,
. sync_read = smb2_sync_read ,
. sync_write = smb2_sync_write ,
. query_dir_first = smb2_query_dir_first ,
. query_dir_next = smb2_query_dir_next ,
. close_dir = smb2_close_dir ,
. calc_smb_size = smb2_calc_size ,
. is_status_pending = smb2_is_status_pending ,
2017-07-09 00:32:00 +03:00
. is_session_expired = smb2_is_session_expired ,
2013-09-05 16:11:28 +04:00
. oplock_response = smb2_oplock_response ,
. queryfs = smb2_queryfs ,
. mand_lock = smb2_mand_lock ,
. mand_unlock_range = smb2_unlock_range ,
. push_mand_locks = smb2_push_mandatory_locks ,
. get_lease_key = smb2_get_lease_key ,
. set_lease_key = smb2_set_lease_key ,
. new_lease_key = smb2_new_lease_key ,
. calc_signature = smb2_calc_signature ,
. is_read_op = smb21_is_read_op ,
. set_oplock_level = smb21_set_oplock_level ,
2013-09-04 13:07:41 +04:00
. create_lease_buf = smb2_create_lease_buf ,
2013-09-05 20:16:45 +04:00
. parse_lease_buf = smb2_parse_lease_buf ,
2017-04-04 10:12:04 +03:00
. copychunk_range = smb2_copychunk_range ,
2014-06-22 11:03:22 +04:00
. wp_retry_size = smb2_wp_retry_size ,
2014-08-18 20:49:57 +04:00
. dir_needs_close = smb2_dir_needs_close ,
2016-10-01 05:14:26 +03:00
. enum_snapshots = smb3_enum_snapshots ,
2017-02-13 18:16:49 +03:00
. get_dfs_refer = smb2_get_dfs_refer ,
2017-01-18 13:05:57 +03:00
. select_sectype = smb2_select_sectype ,
2017-08-24 04:24:55 +03:00
# ifdef CONFIG_CIFS_XATTR
. query_all_EAs = smb2_query_eas ,
2017-08-24 04:24:56 +03:00
. set_EA = smb2_set_ea ,
2017-08-24 04:24:55 +03:00
# endif /* CIFS_XATTR */
2017-06-23 06:52:05 +03:00
. get_acl = get_smb2_acl ,
. get_acl_by_fid = get_smb2_acl_by_fid ,
2017-06-29 06:37:32 +03:00
. set_acl = set_smb2_acl ,
2018-06-01 03:53:08 +03:00
. next_header = smb2_next_header ,
2018-10-08 03:19:58 +03:00
. ioctl_query_info = smb2_ioctl_query_info ,
2019-03-14 08:29:17 +03:00
. make_node = smb2_make_node ,
2019-04-25 09:45:29 +03:00
. fiemap = smb3_fiemap ,
2019-05-15 00:17:02 +03:00
. llseek = smb3_llseek ,
2013-09-05 16:11:28 +04:00
} ;
2012-12-09 08:08:06 +04:00
struct smb_version_operations smb30_operations = {
. compare_fids = smb2_compare_fids ,
. setup_request = smb2_setup_request ,
. setup_async_request = smb2_setup_async_request ,
. check_receive = smb2_check_receive ,
. add_credits = smb2_add_credits ,
. set_credits = smb2_set_credits ,
. get_credits_field = smb2_get_credits_field ,
. get_credits = smb2_get_credits ,
2014-06-05 19:03:27 +04:00
. wait_mtu_credits = smb2_wait_mtu_credits ,
2019-01-24 05:15:52 +03:00
. adjust_credits = smb2_adjust_credits ,
2012-12-09 08:08:06 +04:00
. get_next_mid = smb2_get_next_mid ,
2019-03-05 01:02:50 +03:00
. revert_current_mid = smb2_revert_current_mid ,
2012-12-09 08:08:06 +04:00
. read_data_offset = smb2_read_data_offset ,
. read_data_length = smb2_read_data_length ,
. map_error = map_smb2_to_linux_error ,
. find_mid = smb2_find_mid ,
. check_message = smb2_check_message ,
. dump_detail = smb2_dump_detail ,
. clear_stats = smb2_clear_stats ,
. print_stats = smb2_print_stats ,
2013-06-19 23:15:30 +04:00
. dump_share_caps = smb2_dump_share_caps ,
2012-12-09 08:08:06 +04:00
. is_oplock_break = smb2_is_valid_oplock_break ,
2017-03-04 02:41:38 +03:00
. handle_cancelled_mid = smb2_handle_cancelled_mid ,
2019-02-14 02:43:08 +03:00
. downgrade_oplock = smb21_downgrade_oplock ,
2012-12-09 08:08:06 +04:00
. need_neg = smb2_need_neg ,
. negotiate = smb2_negotiate ,
2018-09-25 23:33:47 +03:00
. negotiate_wsize = smb3_negotiate_wsize ,
. negotiate_rsize = smb3_negotiate_rsize ,
2012-12-09 08:08:06 +04:00
. sess_setup = SMB2_sess_setup ,
. logoff = SMB2_logoff ,
. tree_connect = SMB2_tcon ,
. tree_disconnect = SMB2_tdis ,
2013-10-10 05:55:53 +04:00
. qfs_tcon = smb3_qfs_tcon ,
2012-12-09 08:08:06 +04:00
. is_path_accessible = smb2_is_path_accessible ,
. can_echo = smb2_can_echo ,
. echo = SMB2_echo ,
. query_path_info = smb2_query_path_info ,
. get_srv_inum = smb2_get_srv_inum ,
. query_file_info = smb2_query_file_info ,
. set_path_size = smb2_set_path_size ,
. set_file_size = smb2_set_file_size ,
. set_file_info = smb2_set_file_info ,
2013-10-15 00:31:32 +04:00
. set_compression = smb2_set_compression ,
2012-12-09 08:08:06 +04:00
. mkdir = smb2_mkdir ,
. mkdir_setinfo = smb2_mkdir_setinfo ,
. rmdir = smb2_rmdir ,
. unlink = smb2_unlink ,
. rename = smb2_rename_path ,
. create_hardlink = smb2_create_hardlink ,
2013-08-14 19:25:21 +04:00
. query_symlink = smb2_query_symlink ,
2014-09-16 16:18:19 +04:00
. query_mf_symlink = smb3_query_mf_symlink ,
2014-09-15 13:49:28 +04:00
. create_mf_symlink = smb3_create_mf_symlink ,
2012-12-09 08:08:06 +04:00
. open = smb2_open_file ,
. set_fid = smb2_set_fid ,
. close = smb2_close_file ,
. flush = smb2_flush_file ,
. async_readv = smb2_async_readv ,
. async_writev = smb2_async_writev ,
. sync_read = smb2_sync_read ,
. sync_write = smb2_sync_write ,
. query_dir_first = smb2_query_dir_first ,
. query_dir_next = smb2_query_dir_next ,
. close_dir = smb2_close_dir ,
. calc_smb_size = smb2_calc_size ,
. is_status_pending = smb2_is_status_pending ,
2017-07-09 00:32:00 +03:00
. is_session_expired = smb2_is_session_expired ,
2012-12-09 08:08:06 +04:00
. oplock_response = smb2_oplock_response ,
. queryfs = smb2_queryfs ,
. mand_lock = smb2_mand_lock ,
. mand_unlock_range = smb2_unlock_range ,
. push_mand_locks = smb2_push_mandatory_locks ,
. get_lease_key = smb2_get_lease_key ,
. set_lease_key = smb2_set_lease_key ,
. new_lease_key = smb2_new_lease_key ,
2015-12-18 22:05:30 +03:00
. generate_signingkey = generate_smb30signingkey ,
2012-12-09 08:08:06 +04:00
. calc_signature = smb3_calc_signature ,
2015-06-24 11:17:02 +03:00
. set_integrity = smb3_set_integrity ,
2013-09-05 16:11:28 +04:00
. is_read_op = smb21_is_read_op ,
2013-09-05 21:30:16 +04:00
. set_oplock_level = smb3_set_oplock_level ,
2013-09-04 13:44:05 +04:00
. create_lease_buf = smb3_create_lease_buf ,
. parse_lease_buf = smb3_parse_lease_buf ,
2017-04-04 10:12:04 +03:00
. copychunk_range = smb2_copychunk_range ,
2015-10-02 05:40:10 +03:00
. duplicate_extents = smb2_duplicate_extents ,
2013-11-20 09:44:46 +04:00
. validate_negotiate = smb3_validate_negotiate ,
2014-06-22 11:03:22 +04:00
. wp_retry_size = smb2_wp_retry_size ,
2014-08-18 20:49:57 +04:00
. dir_needs_close = smb2_dir_needs_close ,
2014-08-17 17:38:47 +04:00
. fallocate = smb3_fallocate ,
2016-10-01 05:14:26 +03:00
. enum_snapshots = smb3_enum_snapshots ,
2016-11-04 02:47:37 +03:00
. init_transform_rq = smb3_init_transform_rq ,
2016-11-18 02:24:46 +03:00
. is_transform_hdr = smb3_is_transform_hdr ,
. receive_transform = smb3_receive_transform ,
2017-02-13 18:16:49 +03:00
. get_dfs_refer = smb2_get_dfs_refer ,
2017-01-18 13:05:57 +03:00
. select_sectype = smb2_select_sectype ,
2017-08-24 04:24:55 +03:00
# ifdef CONFIG_CIFS_XATTR
. query_all_EAs = smb2_query_eas ,
2017-08-24 04:24:56 +03:00
. set_EA = smb2_set_ea ,
2017-08-24 04:24:55 +03:00
# endif /* CIFS_XATTR */
2017-06-23 06:52:05 +03:00
. get_acl = get_smb2_acl ,
. get_acl_by_fid = get_smb2_acl_by_fid ,
2017-06-29 06:37:32 +03:00
. set_acl = set_smb2_acl ,
2018-06-01 03:53:08 +03:00
. next_header = smb2_next_header ,
2018-10-08 03:19:58 +03:00
. ioctl_query_info = smb2_ioctl_query_info ,
2019-03-14 08:29:17 +03:00
. make_node = smb2_make_node ,
2019-04-25 09:45:29 +03:00
. fiemap = smb3_fiemap ,
2019-05-15 00:17:02 +03:00
. llseek = smb3_llseek ,
2011-02-24 21:07:19 +03:00
} ;
2015-06-24 07:37:11 +03:00
struct smb_version_operations smb311_operations = {
. compare_fids = smb2_compare_fids ,
. setup_request = smb2_setup_request ,
. setup_async_request = smb2_setup_async_request ,
. check_receive = smb2_check_receive ,
. add_credits = smb2_add_credits ,
. set_credits = smb2_set_credits ,
. get_credits_field = smb2_get_credits_field ,
. get_credits = smb2_get_credits ,
. wait_mtu_credits = smb2_wait_mtu_credits ,
2019-01-24 05:15:52 +03:00
. adjust_credits = smb2_adjust_credits ,
2015-06-24 07:37:11 +03:00
. get_next_mid = smb2_get_next_mid ,
2019-03-05 01:02:50 +03:00
. revert_current_mid = smb2_revert_current_mid ,
2015-06-24 07:37:11 +03:00
. read_data_offset = smb2_read_data_offset ,
. read_data_length = smb2_read_data_length ,
. map_error = map_smb2_to_linux_error ,
. find_mid = smb2_find_mid ,
. check_message = smb2_check_message ,
. dump_detail = smb2_dump_detail ,
. clear_stats = smb2_clear_stats ,
. print_stats = smb2_print_stats ,
. dump_share_caps = smb2_dump_share_caps ,
. is_oplock_break = smb2_is_valid_oplock_break ,
2017-03-04 02:41:38 +03:00
. handle_cancelled_mid = smb2_handle_cancelled_mid ,
2019-02-14 02:43:08 +03:00
. downgrade_oplock = smb21_downgrade_oplock ,
2015-06-24 07:37:11 +03:00
. need_neg = smb2_need_neg ,
. negotiate = smb2_negotiate ,
2018-09-25 23:33:47 +03:00
. negotiate_wsize = smb3_negotiate_wsize ,
. negotiate_rsize = smb3_negotiate_rsize ,
2015-06-24 07:37:11 +03:00
. sess_setup = SMB2_sess_setup ,
. logoff = SMB2_logoff ,
. tree_connect = SMB2_tcon ,
. tree_disconnect = SMB2_tdis ,
. qfs_tcon = smb3_qfs_tcon ,
. is_path_accessible = smb2_is_path_accessible ,
. can_echo = smb2_can_echo ,
. echo = SMB2_echo ,
. query_path_info = smb2_query_path_info ,
. get_srv_inum = smb2_get_srv_inum ,
. query_file_info = smb2_query_file_info ,
. set_path_size = smb2_set_path_size ,
. set_file_size = smb2_set_file_size ,
. set_file_info = smb2_set_file_info ,
. set_compression = smb2_set_compression ,
. mkdir = smb2_mkdir ,
. mkdir_setinfo = smb2_mkdir_setinfo ,
2018-06-15 05:56:32 +03:00
. posix_mkdir = smb311_posix_mkdir ,
2015-06-24 07:37:11 +03:00
. rmdir = smb2_rmdir ,
. unlink = smb2_unlink ,
. rename = smb2_rename_path ,
. create_hardlink = smb2_create_hardlink ,
. query_symlink = smb2_query_symlink ,
. query_mf_symlink = smb3_query_mf_symlink ,
. create_mf_symlink = smb3_create_mf_symlink ,
. open = smb2_open_file ,
. set_fid = smb2_set_fid ,
. close = smb2_close_file ,
. flush = smb2_flush_file ,
. async_readv = smb2_async_readv ,
. async_writev = smb2_async_writev ,
. sync_read = smb2_sync_read ,
. sync_write = smb2_sync_write ,
. query_dir_first = smb2_query_dir_first ,
. query_dir_next = smb2_query_dir_next ,
. close_dir = smb2_close_dir ,
. calc_smb_size = smb2_calc_size ,
. is_status_pending = smb2_is_status_pending ,
2017-07-09 00:32:00 +03:00
. is_session_expired = smb2_is_session_expired ,
2015-06-24 07:37:11 +03:00
. oplock_response = smb2_oplock_response ,
2018-06-25 07:28:12 +03:00
. queryfs = smb311_queryfs ,
2015-06-24 07:37:11 +03:00
. mand_lock = smb2_mand_lock ,
. mand_unlock_range = smb2_unlock_range ,
. push_mand_locks = smb2_push_mandatory_locks ,
. get_lease_key = smb2_get_lease_key ,
. set_lease_key = smb2_set_lease_key ,
. new_lease_key = smb2_new_lease_key ,
2015-12-18 22:05:30 +03:00
. generate_signingkey = generate_smb311signingkey ,
2015-06-24 07:37:11 +03:00
. calc_signature = smb3_calc_signature ,
2015-06-24 11:17:02 +03:00
. set_integrity = smb3_set_integrity ,
2015-06-24 07:37:11 +03:00
. is_read_op = smb21_is_read_op ,
. set_oplock_level = smb3_set_oplock_level ,
. create_lease_buf = smb3_create_lease_buf ,
. parse_lease_buf = smb3_parse_lease_buf ,
2017-04-04 10:12:04 +03:00
. copychunk_range = smb2_copychunk_range ,
2015-06-28 07:18:36 +03:00
. duplicate_extents = smb2_duplicate_extents ,
2015-06-24 07:37:11 +03:00
/* .validate_negotiate = smb3_validate_negotiate, */ /* not used in 3.11 */
. wp_retry_size = smb2_wp_retry_size ,
. dir_needs_close = smb2_dir_needs_close ,
. fallocate = smb3_fallocate ,
2016-10-01 05:14:26 +03:00
. enum_snapshots = smb3_enum_snapshots ,
2016-11-04 02:47:37 +03:00
. init_transform_rq = smb3_init_transform_rq ,
2016-11-18 02:24:46 +03:00
. is_transform_hdr = smb3_is_transform_hdr ,
. receive_transform = smb3_receive_transform ,
2017-02-13 18:16:49 +03:00
. get_dfs_refer = smb2_get_dfs_refer ,
2017-01-18 13:05:57 +03:00
. select_sectype = smb2_select_sectype ,
2017-08-24 04:24:55 +03:00
# ifdef CONFIG_CIFS_XATTR
. query_all_EAs = smb2_query_eas ,
2017-08-24 04:24:56 +03:00
. set_EA = smb2_set_ea ,
2017-08-24 04:24:55 +03:00
# endif /* CIFS_XATTR */
2018-08-10 04:03:55 +03:00
. get_acl = get_smb2_acl ,
. get_acl_by_fid = get_smb2_acl_by_fid ,
. set_acl = set_smb2_acl ,
2018-06-01 03:53:08 +03:00
. next_header = smb2_next_header ,
2018-10-08 03:19:58 +03:00
. ioctl_query_info = smb2_ioctl_query_info ,
2019-03-14 08:29:17 +03:00
. make_node = smb2_make_node ,
2019-04-25 09:45:29 +03:00
. fiemap = smb3_fiemap ,
2019-05-15 00:17:02 +03:00
. llseek = smb3_llseek ,
2015-06-24 07:37:11 +03:00
} ;
2012-11-29 09:21:06 +04:00
struct smb_version_values smb20_values = {
. version_string = SMB20_VERSION_STRING ,
. protocol_id = SMB20_PROT_ID ,
. req_capabilities = 0 , /* MBZ */
. large_lock_type = 0 ,
. exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK ,
. shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK ,
. unlock_lock_type = SMB2_LOCKFLAG_UNLOCK ,
2018-06-01 03:53:02 +03:00
. header_size = sizeof ( struct smb2_sync_hdr ) ,
. header_preamble_size = 0 ,
2012-11-29 09:21:06 +04:00
. max_header_size = MAX_SMB2_HDR_SIZE ,
. read_rsp_size = sizeof ( struct smb2_read_rsp ) - 1 ,
. lock_cmd = SMB2_LOCK ,
. cap_unix = 0 ,
. cap_nt_find = SMB2_NT_FIND ,
. cap_large_files = SMB2_LARGE_FILES ,
2013-06-27 20:45:00 +04:00
. signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED ,
. signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED ,
2013-09-04 13:07:41 +04:00
. create_lease_size = sizeof ( struct create_lease ) ,
2012-11-29 09:21:06 +04:00
} ;
2011-02-24 21:07:19 +03:00
struct smb_version_values smb21_values = {
. version_string = SMB21_VERSION_STRING ,
2012-10-01 21:26:22 +04:00
. protocol_id = SMB21_PROT_ID ,
. req_capabilities = 0 , /* MBZ on negotiate req until SMB3 dialect */
. large_lock_type = 0 ,
. exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK ,
. shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK ,
. unlock_lock_type = SMB2_LOCKFLAG_UNLOCK ,
2018-06-01 03:53:02 +03:00
. header_size = sizeof ( struct smb2_sync_hdr ) ,
. header_preamble_size = 0 ,
2012-10-01 21:26:22 +04:00
. max_header_size = MAX_SMB2_HDR_SIZE ,
. read_rsp_size = sizeof ( struct smb2_read_rsp ) - 1 ,
. lock_cmd = SMB2_LOCK ,
. cap_unix = 0 ,
. cap_nt_find = SMB2_NT_FIND ,
. cap_large_files = SMB2_LARGE_FILES ,
2013-06-27 20:45:00 +04:00
. signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED ,
. signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED ,
2013-09-04 13:07:41 +04:00
. create_lease_size = sizeof ( struct create_lease ) ,
2012-10-01 21:26:22 +04:00
} ;
2017-09-17 18:41:35 +03:00
struct smb_version_values smb3any_values = {
. version_string = SMB3ANY_VERSION_STRING ,
. protocol_id = SMB302_PROT_ID , /* doesn't matter, send protocol array */
2018-08-31 23:12:10 +03:00
. req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING ,
2017-09-17 18:41:35 +03:00
. large_lock_type = 0 ,
. exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK ,
. shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK ,
. unlock_lock_type = SMB2_LOCKFLAG_UNLOCK ,
2018-06-01 03:53:02 +03:00
. header_size = sizeof ( struct smb2_sync_hdr ) ,
. header_preamble_size = 0 ,
2017-09-17 18:41:35 +03:00
. max_header_size = MAX_SMB2_HDR_SIZE ,
. read_rsp_size = sizeof ( struct smb2_read_rsp ) - 1 ,
. lock_cmd = SMB2_LOCK ,
. cap_unix = 0 ,
. cap_nt_find = SMB2_NT_FIND ,
. cap_large_files = SMB2_LARGE_FILES ,
. signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED ,
. signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED ,
. create_lease_size = sizeof ( struct create_lease_v2 ) ,
} ;
struct smb_version_values smbdefault_values = {
. version_string = SMBDEFAULT_VERSION_STRING ,
. protocol_id = SMB302_PROT_ID , /* doesn't matter, send protocol array */
2018-08-31 23:12:10 +03:00
. req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING ,
2017-09-17 18:41:35 +03:00
. large_lock_type = 0 ,
. exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK ,
. shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK ,
. unlock_lock_type = SMB2_LOCKFLAG_UNLOCK ,
2018-06-01 03:53:02 +03:00
. header_size = sizeof ( struct smb2_sync_hdr ) ,
. header_preamble_size = 0 ,
2017-09-17 18:41:35 +03:00
. max_header_size = MAX_SMB2_HDR_SIZE ,
. read_rsp_size = sizeof ( struct smb2_read_rsp ) - 1 ,
. lock_cmd = SMB2_LOCK ,
. cap_unix = 0 ,
. cap_nt_find = SMB2_NT_FIND ,
. cap_large_files = SMB2_LARGE_FILES ,
. signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED ,
. signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED ,
. create_lease_size = sizeof ( struct create_lease_v2 ) ,
} ;
2012-10-01 21:26:22 +04:00
struct smb_version_values smb30_values = {
. version_string = SMB30_VERSION_STRING ,
. protocol_id = SMB30_PROT_ID ,
2018-08-31 23:12:10 +03:00
. req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING ,
2012-09-19 17:22:43 +04:00
. large_lock_type = 0 ,
. exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK ,
. shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK ,
. unlock_lock_type = SMB2_LOCKFLAG_UNLOCK ,
2018-06-01 03:53:02 +03:00
. header_size = sizeof ( struct smb2_sync_hdr ) ,
. header_preamble_size = 0 ,
2011-06-08 15:51:07 +04:00
. 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 ,
2013-06-27 20:45:00 +04:00
. signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED ,
. signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED ,
2013-09-04 13:44:05 +04:00
. create_lease_size = sizeof ( struct create_lease_v2 ) ,
2011-02-24 21:07:19 +03:00
} ;
2013-06-13 07:48:41 +04:00
struct smb_version_values smb302_values = {
. version_string = SMB302_VERSION_STRING ,
. protocol_id = SMB302_PROT_ID ,
2018-08-31 23:12:10 +03:00
. req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING ,
2013-06-13 07:48:41 +04:00
. large_lock_type = 0 ,
. exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK ,
. shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK ,
. unlock_lock_type = SMB2_LOCKFLAG_UNLOCK ,
2018-06-01 03:53:02 +03:00
. header_size = sizeof ( struct smb2_sync_hdr ) ,
. header_preamble_size = 0 ,
2013-06-13 07:48:41 +04:00
. max_header_size = MAX_SMB2_HDR_SIZE ,
. read_rsp_size = sizeof ( struct smb2_read_rsp ) - 1 ,
. lock_cmd = SMB2_LOCK ,
. cap_unix = 0 ,
. cap_nt_find = SMB2_NT_FIND ,
. cap_large_files = SMB2_LARGE_FILES ,
2013-06-27 20:45:00 +04:00
. signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED ,
. signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED ,
2013-09-04 13:44:05 +04:00
. create_lease_size = sizeof ( struct create_lease_v2 ) ,
2013-06-13 07:48:41 +04:00
} ;
2014-12-18 07:52:58 +03:00
struct smb_version_values smb311_values = {
. version_string = SMB311_VERSION_STRING ,
. protocol_id = SMB311_PROT_ID ,
2018-08-31 23:12:10 +03:00
. req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION | SMB2_GLOBAL_CAP_DIRECTORY_LEASING ,
2014-12-18 07:52:58 +03:00
. large_lock_type = 0 ,
. exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK ,
. shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK ,
. unlock_lock_type = SMB2_LOCKFLAG_UNLOCK ,
2018-06-01 03:53:02 +03:00
. header_size = sizeof ( struct smb2_sync_hdr ) ,
. header_preamble_size = 0 ,
2014-12-18 07:52:58 +03:00
. max_header_size = MAX_SMB2_HDR_SIZE ,
. read_rsp_size = sizeof ( struct smb2_read_rsp ) - 1 ,
. lock_cmd = SMB2_LOCK ,
. cap_unix = 0 ,
. cap_nt_find = SMB2_NT_FIND ,
. cap_large_files = SMB2_LARGE_FILES ,
. signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED ,
. signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED ,
. create_lease_size = sizeof ( struct create_lease_v2 ) ,
} ;