2011-12-27 16:12:43 +04:00
/*
* fs / cifs / smb2pdu . c
*
2013-06-24 03:43:37 +04:00
* Copyright ( C ) International Business Machines Corp . , 2009 , 2013
2011-12-27 16:12:43 +04:00
* Etersoft , 2012
* Author ( s ) : Steve French ( sfrench @ us . ibm . com )
* Pavel Shilovsky ( pshilovsky @ samba . org ) 2012
*
* Contains the routines for constructing the SMB2 PDUs themselves
*
* This library is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation ; either version 2.1 of the License , or
* ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See
* the GNU Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
/* SMB2 PDU handling routines here - except for leftovers (eg session setup) */
/* Note that there are handle based routines which must be */
/* treated slightly differently for reconnection purposes since we never */
/* want to reuse a stale file handle and only the caller knows the file info */
# include <linux/fs.h>
# include <linux/kernel.h>
# include <linux/vfs.h>
2012-09-19 03:20:29 +04:00
# include <linux/task_io_accounting_ops.h>
2011-12-27 16:12:43 +04:00
# include <linux/uaccess.h>
2017-03-29 00:45:06 +03:00
# include <linux/uuid.h>
2012-09-19 03:20:29 +04:00
# include <linux/pagemap.h>
2011-12-27 16:12:43 +04:00
# include <linux/xattr.h>
# include "smb2pdu.h"
# include "cifsglob.h"
# include "cifsacl.h"
# include "cifsproto.h"
# include "smb2proto.h"
# include "cifs_unicode.h"
# include "cifs_debug.h"
# include "ntlmssp.h"
# include "smb2status.h"
2012-09-19 03:20:29 +04:00
# include "smb2glob.h"
2012-09-19 03:20:33 +04:00
# include "cifspdu.h"
2015-09-24 08:52:37 +03:00
# include "cifs_spnego.h"
2017-11-23 03:38:45 +03:00
# include "smbdirect.h"
2018-05-18 05:16:55 +03:00
# include "trace.h"
2018-11-14 22:20:31 +03:00
# ifdef CONFIG_CIFS_DFS_UPCALL
# include "dfs_cache.h"
# endif
2011-12-27 16:12:43 +04:00
/*
* The following table defines the expected " StructureSize " of SMB2 requests
* in order by SMB2 command . This is similar to " wct " in SMB / CIFS requests .
*
* Note that commands are defined in smb2pdu . h in le16 but the array below is
* indexed by command in host byte order .
*/
static const int smb2_req_struct_sizes [ NUMBER_OF_SMB2_COMMANDS ] = {
/* SMB2_NEGOTIATE */ 36 ,
/* SMB2_SESSION_SETUP */ 25 ,
/* SMB2_LOGOFF */ 4 ,
/* SMB2_TREE_CONNECT */ 9 ,
/* SMB2_TREE_DISCONNECT */ 4 ,
/* SMB2_CREATE */ 57 ,
/* SMB2_CLOSE */ 24 ,
/* SMB2_FLUSH */ 24 ,
/* SMB2_READ */ 49 ,
/* SMB2_WRITE */ 49 ,
/* SMB2_LOCK */ 48 ,
/* SMB2_IOCTL */ 57 ,
/* SMB2_CANCEL */ 4 ,
/* SMB2_ECHO */ 4 ,
/* SMB2_QUERY_DIRECTORY */ 33 ,
/* SMB2_CHANGE_NOTIFY */ 32 ,
/* SMB2_QUERY_INFO */ 41 ,
/* SMB2_SET_INFO */ 33 ,
/* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */
} ;
2018-08-08 08:07:49 +03:00
int smb3_encryption_required ( const struct cifs_tcon * tcon )
2016-10-31 23:49:30 +03:00
{
2016-11-18 00:59:23 +03:00
if ( ! tcon )
return 0 ;
2016-10-31 23:49:30 +03:00
if ( ( tcon - > ses - > session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA ) | |
( tcon - > share_flags & SHI1005_FLAGS_ENCRYPT_DATA ) )
return 1 ;
2016-11-18 00:59:23 +03:00
if ( tcon - > seal & &
( tcon - > ses - > server - > capabilities & SMB2_GLOBAL_CAP_ENCRYPTION ) )
return 1 ;
2016-10-31 23:49:30 +03:00
return 0 ;
}
2011-12-27 16:12:43 +04:00
static void
2016-10-25 02:59:57 +03:00
smb2_hdr_assemble ( struct smb2_sync_hdr * shdr , __le16 smb2_cmd ,
2011-12-27 16:12:43 +04:00
const struct cifs_tcon * tcon )
{
2016-10-25 01:33:04 +03:00
shdr - > ProtocolId = SMB2_PROTO_NUMBER ;
shdr - > StructureSize = cpu_to_le16 ( 64 ) ;
shdr - > Command = smb2_cmd ;
2016-09-20 15:37:13 +03:00
if ( tcon & & tcon - > ses & & tcon - > ses - > server ) {
struct TCP_Server_Info * server = tcon - > ses - > server ;
spin_lock ( & server - > req_lock ) ;
/* Request up to 2 credits but don't go over the limit. */
2016-09-23 08:44:16 +03:00
if ( server - > credits > = server - > max_credits )
2016-10-25 01:33:04 +03:00
shdr - > CreditRequest = cpu_to_le16 ( 0 ) ;
2016-09-20 15:37:13 +03:00
else
2016-10-25 01:33:04 +03:00
shdr - > CreditRequest = cpu_to_le16 (
2016-09-23 08:44:16 +03:00
min_t ( int , server - > max_credits -
2016-09-20 15:37:13 +03:00
server - > credits , 2 ) ) ;
spin_unlock ( & server - > req_lock ) ;
} else {
2016-10-25 01:33:04 +03:00
shdr - > CreditRequest = cpu_to_le16 ( 2 ) ;
2016-09-20 15:37:13 +03:00
}
2016-10-25 01:33:04 +03:00
shdr - > ProcessId = cpu_to_le32 ( ( __u16 ) current - > tgid ) ;
2011-12-27 16:12:43 +04:00
if ( ! tcon )
goto out ;
2013-06-24 03:43:37 +04:00
/* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */
/* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */
2015-05-20 17:32:21 +03:00
if ( ( tcon - > ses ) & & ( tcon - > ses - > server ) & &
2013-06-27 02:52:17 +04:00
( tcon - > ses - > server - > capabilities & SMB2_GLOBAL_CAP_LARGE_MTU ) )
2016-10-25 01:33:04 +03:00
shdr - > CreditCharge = cpu_to_le16 ( 1 ) ;
2013-06-24 03:43:37 +04:00
/* else CreditCharge MBZ */
2016-10-25 01:33:04 +03:00
shdr - > TreeId = tcon - > tid ;
2011-12-27 16:12:43 +04:00
/* Uid is not converted */
if ( tcon - > ses )
2016-10-25 01:33:04 +03:00
shdr - > SessionId = tcon - > ses - > Suid ;
2013-06-27 04:14:55 +04:00
/*
* If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have
* to pass the path on the Open SMB prefixed by \ \ server \ share .
* Not sure when we would need to do the augmented path ( if ever ) and
* setting this flag breaks the SMB2 open operation since it is
* illegal to send an empty path name ( without \ \ server \ share prefix )
* when the DFS flag is set in the SMB open header . We could
* consider setting the flag on all operations other than open
* but it is safer to net set it for now .
*/
/* if (tcon->share_flags & SHI1005_FLAGS_DFS)
2016-10-25 01:33:04 +03:00
shdr - > Flags | = SMB2_FLAGS_DFS_OPERATIONS ; */
2013-06-27 04:14:55 +04:00
2016-10-31 23:49:30 +03:00
if ( tcon - > ses & & tcon - > ses - > server & & tcon - > ses - > server - > sign & &
2018-05-10 01:43:08 +03:00
! smb3_encryption_required ( tcon ) )
2016-10-25 01:33:04 +03:00
shdr - > Flags | = SMB2_FLAGS_SIGNED ;
2011-12-27 16:12:43 +04:00
out :
return ;
}
2018-11-14 22:20:31 +03:00
# ifdef CONFIG_CIFS_DFS_UPCALL
static int __smb2_reconnect ( const struct nls_table * nlsc ,
struct cifs_tcon * tcon )
{
int rc ;
struct dfs_cache_tgt_list tl ;
struct dfs_cache_tgt_iterator * it = NULL ;
2019-01-08 15:41:00 +03:00
char * tree ;
2018-11-14 22:20:31 +03:00
const char * tcp_host ;
size_t tcp_host_len ;
const char * dfs_host ;
size_t dfs_host_len ;
2019-01-08 15:41:00 +03:00
tree = kzalloc ( MAX_TREE_SIZE , GFP_KERNEL ) ;
if ( ! tree )
return - ENOMEM ;
2018-11-14 22:20:31 +03:00
if ( tcon - > ipc ) {
2019-01-08 15:41:00 +03:00
snprintf ( tree , MAX_TREE_SIZE , " \\ \\ %s \\ IPC$ " ,
2018-11-14 22:20:31 +03:00
tcon - > ses - > server - > hostname ) ;
2019-01-08 15:41:00 +03:00
rc = SMB2_tcon ( 0 , tcon - > ses , tree , tcon , nlsc ) ;
goto out ;
2018-11-14 22:20:31 +03:00
}
2019-01-08 15:41:00 +03:00
if ( ! tcon - > dfs_path ) {
rc = SMB2_tcon ( 0 , tcon - > ses , tcon - > treeName , tcon , nlsc ) ;
goto out ;
}
2018-11-14 22:20:31 +03:00
rc = dfs_cache_noreq_find ( tcon - > dfs_path + 1 , NULL , & tl ) ;
if ( rc )
2019-01-08 15:41:00 +03:00
goto out ;
2018-11-14 22:20:31 +03:00
extract_unc_hostname ( tcon - > ses - > server - > hostname , & tcp_host ,
& tcp_host_len ) ;
for ( it = dfs_cache_get_tgt_iterator ( & tl ) ; it ;
it = dfs_cache_get_next_tgt ( & tl , it ) ) {
const char * tgt = dfs_cache_get_tgt_name ( it ) ;
extract_unc_hostname ( tgt , & dfs_host , & dfs_host_len ) ;
if ( dfs_host_len ! = tcp_host_len
| | strncasecmp ( dfs_host , tcp_host , dfs_host_len ) ! = 0 ) {
cifs_dbg ( FYI , " %s: skipping %.*s, doesn't match %.*s " ,
__func__ ,
( int ) dfs_host_len , dfs_host ,
( int ) tcp_host_len , tcp_host ) ;
continue ;
}
2019-01-08 15:41:00 +03:00
snprintf ( tree , MAX_TREE_SIZE , " \\ %s " , tgt ) ;
2018-11-14 22:20:31 +03:00
rc = SMB2_tcon ( 0 , tcon - > ses , tree , tcon , nlsc ) ;
if ( ! rc )
break ;
if ( rc = = - EREMOTE )
break ;
}
if ( ! rc ) {
if ( it )
rc = dfs_cache_noreq_update_tgthint ( tcon - > dfs_path + 1 ,
it ) ;
else
rc = - ENOENT ;
}
dfs_cache_free_tgts ( & tl ) ;
2019-01-08 15:41:00 +03:00
out :
kfree ( tree ) ;
2018-11-14 22:20:31 +03:00
return rc ;
}
# else
static inline int __smb2_reconnect ( const struct nls_table * nlsc ,
struct cifs_tcon * tcon )
{
return SMB2_tcon ( 0 , tcon - > ses , tcon - > treeName , tcon , nlsc ) ;
}
# endif
2011-12-27 16:12:43 +04:00
static int
smb2_reconnect ( __le16 smb2_command , struct cifs_tcon * tcon )
{
cifs: Fix infinite loop when using hard mount option
For every request we send, whether it is SMB1 or SMB2+, we attempt to
reconnect tcon (cifs_reconnect_tcon or smb2_reconnect) before carrying
out the request.
So, while server->tcpStatus != CifsNeedReconnect, we wait for the
reconnection to succeed on wait_event_interruptible_timeout(). If it
returns, that means that either the condition was evaluated to true, or
timeout elapsed, or it was interrupted by a signal.
Since we're not handling the case where the process woke up due to a
received signal (-ERESTARTSYS), the next call to
wait_event_interruptible_timeout() will _always_ fail and we end up
looping forever inside either cifs_reconnect_tcon() or smb2_reconnect().
Here's an example of how to trigger that:
$ mount.cifs //foo/share /mnt/test -o
username=foo,password=foo,vers=1.0,hard
(break connection to server before executing bellow cmd)
$ stat -f /mnt/test & sleep 140
[1] 2511
$ ps -aux -q 2511
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2511 0.0 0.0 12892 1008 pts/0 S 12:24 0:00 stat -f
/mnt/test
$ kill -9 2511
(wait for a while; process is stuck in the kernel)
$ ps -aux -q 2511
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2511 83.2 0.0 12892 1008 pts/0 R 12:24 30:01 stat -f
/mnt/test
By using 'hard' mount point means that cifs.ko will keep retrying
indefinitely, however we must allow the process to be killed otherwise
it would hang the system.
Signed-off-by: Paulo Alcantara <palcantara@suse.de>
Cc: stable@vger.kernel.org
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
2018-07-05 19:46:34 +03:00
int rc ;
2011-12-27 16:23:34 +04:00
struct nls_table * nls_codepage ;
struct cifs_ses * ses ;
struct TCP_Server_Info * server ;
2018-11-14 22:20:31 +03:00
int retries ;
2011-12-27 16:23:34 +04:00
/*
* SMB2s NegProt , SessSetup , Logoff do not have tcon yet so
* check for tcp and smb session status done differently
* for those three - in the calling routine .
*/
if ( tcon = = NULL )
cifs: Fix infinite loop when using hard mount option
For every request we send, whether it is SMB1 or SMB2+, we attempt to
reconnect tcon (cifs_reconnect_tcon or smb2_reconnect) before carrying
out the request.
So, while server->tcpStatus != CifsNeedReconnect, we wait for the
reconnection to succeed on wait_event_interruptible_timeout(). If it
returns, that means that either the condition was evaluated to true, or
timeout elapsed, or it was interrupted by a signal.
Since we're not handling the case where the process woke up due to a
received signal (-ERESTARTSYS), the next call to
wait_event_interruptible_timeout() will _always_ fail and we end up
looping forever inside either cifs_reconnect_tcon() or smb2_reconnect().
Here's an example of how to trigger that:
$ mount.cifs //foo/share /mnt/test -o
username=foo,password=foo,vers=1.0,hard
(break connection to server before executing bellow cmd)
$ stat -f /mnt/test & sleep 140
[1] 2511
$ ps -aux -q 2511
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2511 0.0 0.0 12892 1008 pts/0 S 12:24 0:00 stat -f
/mnt/test
$ kill -9 2511
(wait for a while; process is stuck in the kernel)
$ ps -aux -q 2511
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2511 83.2 0.0 12892 1008 pts/0 R 12:24 30:01 stat -f
/mnt/test
By using 'hard' mount point means that cifs.ko will keep retrying
indefinitely, however we must allow the process to be killed otherwise
it would hang the system.
Signed-off-by: Paulo Alcantara <palcantara@suse.de>
Cc: stable@vger.kernel.org
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
2018-07-05 19:46:34 +03:00
return 0 ;
2011-12-27 16:23:34 +04:00
if ( smb2_command = = SMB2_TREE_CONNECT )
cifs: Fix infinite loop when using hard mount option
For every request we send, whether it is SMB1 or SMB2+, we attempt to
reconnect tcon (cifs_reconnect_tcon or smb2_reconnect) before carrying
out the request.
So, while server->tcpStatus != CifsNeedReconnect, we wait for the
reconnection to succeed on wait_event_interruptible_timeout(). If it
returns, that means that either the condition was evaluated to true, or
timeout elapsed, or it was interrupted by a signal.
Since we're not handling the case where the process woke up due to a
received signal (-ERESTARTSYS), the next call to
wait_event_interruptible_timeout() will _always_ fail and we end up
looping forever inside either cifs_reconnect_tcon() or smb2_reconnect().
Here's an example of how to trigger that:
$ mount.cifs //foo/share /mnt/test -o
username=foo,password=foo,vers=1.0,hard
(break connection to server before executing bellow cmd)
$ stat -f /mnt/test & sleep 140
[1] 2511
$ ps -aux -q 2511
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2511 0.0 0.0 12892 1008 pts/0 S 12:24 0:00 stat -f
/mnt/test
$ kill -9 2511
(wait for a while; process is stuck in the kernel)
$ ps -aux -q 2511
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2511 83.2 0.0 12892 1008 pts/0 R 12:24 30:01 stat -f
/mnt/test
By using 'hard' mount point means that cifs.ko will keep retrying
indefinitely, however we must allow the process to be killed otherwise
it would hang the system.
Signed-off-by: Paulo Alcantara <palcantara@suse.de>
Cc: stable@vger.kernel.org
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
2018-07-05 19:46:34 +03:00
return 0 ;
2011-12-27 16:23:34 +04:00
if ( tcon - > tidStatus = = CifsExiting ) {
/*
* only tree disconnect , open , and write ,
* ( and ulogoff which does not have tcon )
* are allowed as we start force umount .
*/
if ( ( smb2_command ! = SMB2_WRITE ) & &
( smb2_command ! = SMB2_CREATE ) & &
( smb2_command ! = SMB2_TREE_DISCONNECT ) ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " can not send cmd %d while umounting \n " ,
smb2_command ) ;
2011-12-27 16:23:34 +04:00
return - ENODEV ;
}
}
if ( ( ! tcon - > ses ) | | ( tcon - > ses - > status = = CifsExiting ) | |
( ! tcon - > ses - > server ) )
return - EIO ;
ses = tcon - > ses ;
server = ses - > server ;
2018-11-14 22:20:31 +03:00
retries = server - > nr_targets ;
2011-12-27 16:23:34 +04:00
/*
2018-11-14 22:20:31 +03:00
* Give demultiplex thread up to 10 seconds to each target available for
* reconnect - - should be greater than cifs socket timeout which is 7
* seconds .
2011-12-27 16:23:34 +04:00
*/
while ( server - > tcpStatus = = CifsNeedReconnect ) {
/*
* Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
* here since they are implicitly done when session drops .
*/
switch ( smb2_command ) {
/*
* BB Should we keep oplock break and add flush to exceptions ?
*/
case SMB2_TREE_DISCONNECT :
case SMB2_CANCEL :
case SMB2_CLOSE :
case SMB2_OPLOCK_BREAK :
return - EAGAIN ;
}
cifs: Fix infinite loop when using hard mount option
For every request we send, whether it is SMB1 or SMB2+, we attempt to
reconnect tcon (cifs_reconnect_tcon or smb2_reconnect) before carrying
out the request.
So, while server->tcpStatus != CifsNeedReconnect, we wait for the
reconnection to succeed on wait_event_interruptible_timeout(). If it
returns, that means that either the condition was evaluated to true, or
timeout elapsed, or it was interrupted by a signal.
Since we're not handling the case where the process woke up due to a
received signal (-ERESTARTSYS), the next call to
wait_event_interruptible_timeout() will _always_ fail and we end up
looping forever inside either cifs_reconnect_tcon() or smb2_reconnect().
Here's an example of how to trigger that:
$ mount.cifs //foo/share /mnt/test -o
username=foo,password=foo,vers=1.0,hard
(break connection to server before executing bellow cmd)
$ stat -f /mnt/test & sleep 140
[1] 2511
$ ps -aux -q 2511
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2511 0.0 0.0 12892 1008 pts/0 S 12:24 0:00 stat -f
/mnt/test
$ kill -9 2511
(wait for a while; process is stuck in the kernel)
$ ps -aux -q 2511
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2511 83.2 0.0 12892 1008 pts/0 R 12:24 30:01 stat -f
/mnt/test
By using 'hard' mount point means that cifs.ko will keep retrying
indefinitely, however we must allow the process to be killed otherwise
it would hang the system.
Signed-off-by: Paulo Alcantara <palcantara@suse.de>
Cc: stable@vger.kernel.org
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
2018-07-05 19:46:34 +03:00
rc = wait_event_interruptible_timeout ( server - > response_q ,
( server - > tcpStatus ! = CifsNeedReconnect ) ,
10 * HZ ) ;
if ( rc < 0 ) {
cifs_dbg ( FYI , " %s: aborting reconnect due to a received "
" signal by the process \n " , __func__ ) ;
return - ERESTARTSYS ;
}
2011-12-27 16:23:34 +04:00
/* are we still trying to reconnect? */
if ( server - > tcpStatus ! = CifsNeedReconnect )
break ;
2018-11-14 22:20:31 +03:00
if ( - - retries )
continue ;
2011-12-27 16:23:34 +04:00
/*
* on " soft " mounts we wait once . Hard mounts keep
* retrying until process is killed or server comes
* back on - line
*/
if ( ! tcon - > retry ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " gave up waiting on reconnect in smb_init \n " ) ;
2011-12-27 16:23:34 +04:00
return - EHOSTDOWN ;
}
2018-11-14 22:20:31 +03:00
retries = server - > nr_targets ;
2011-12-27 16:23:34 +04:00
}
if ( ! tcon - > ses - > need_reconnect & & ! tcon - > need_reconnect )
cifs: Fix infinite loop when using hard mount option
For every request we send, whether it is SMB1 or SMB2+, we attempt to
reconnect tcon (cifs_reconnect_tcon or smb2_reconnect) before carrying
out the request.
So, while server->tcpStatus != CifsNeedReconnect, we wait for the
reconnection to succeed on wait_event_interruptible_timeout(). If it
returns, that means that either the condition was evaluated to true, or
timeout elapsed, or it was interrupted by a signal.
Since we're not handling the case where the process woke up due to a
received signal (-ERESTARTSYS), the next call to
wait_event_interruptible_timeout() will _always_ fail and we end up
looping forever inside either cifs_reconnect_tcon() or smb2_reconnect().
Here's an example of how to trigger that:
$ mount.cifs //foo/share /mnt/test -o
username=foo,password=foo,vers=1.0,hard
(break connection to server before executing bellow cmd)
$ stat -f /mnt/test & sleep 140
[1] 2511
$ ps -aux -q 2511
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2511 0.0 0.0 12892 1008 pts/0 S 12:24 0:00 stat -f
/mnt/test
$ kill -9 2511
(wait for a while; process is stuck in the kernel)
$ ps -aux -q 2511
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2511 83.2 0.0 12892 1008 pts/0 R 12:24 30:01 stat -f
/mnt/test
By using 'hard' mount point means that cifs.ko will keep retrying
indefinitely, however we must allow the process to be killed otherwise
it would hang the system.
Signed-off-by: Paulo Alcantara <palcantara@suse.de>
Cc: stable@vger.kernel.org
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
2018-07-05 19:46:34 +03:00
return 0 ;
2011-12-27 16:23:34 +04:00
nls_codepage = load_nls_default ( ) ;
/*
* need to prevent multiple threads trying to simultaneously reconnect
* the same SMB session
*/
mutex_lock ( & tcon - > ses - > session_mutex ) ;
2017-07-11 13:44:39 +03:00
/*
* Recheck after acquire mutex . If another thread is negotiating
* and the server never sends an answer the socket will be closed
* and tcpStatus set to reconnect .
*/
if ( server - > tcpStatus = = CifsNeedReconnect ) {
rc = - EHOSTDOWN ;
mutex_unlock ( & tcon - > ses - > session_mutex ) ;
goto out ;
}
2011-12-27 16:23:34 +04:00
rc = cifs_negotiate_protocol ( 0 , tcon - > ses ) ;
if ( ! rc & & tcon - > ses - > need_reconnect )
rc = cifs_setup_session ( 0 , tcon - > ses , nls_codepage ) ;
if ( rc | | ! tcon - > need_reconnect ) {
mutex_unlock ( & tcon - > ses - > session_mutex ) ;
goto out ;
}
cifs_mark_open_files_invalid ( tcon ) ;
2016-11-29 22:31:23 +03:00
if ( tcon - > use_persistent )
tcon - > need_reopen_files = true ;
2016-09-23 03:23:56 +03:00
2018-11-14 22:20:31 +03:00
rc = __smb2_reconnect ( nls_codepage , tcon ) ;
2011-12-27 16:23:34 +04:00
mutex_unlock ( & tcon - > ses - > session_mutex ) ;
2016-09-23 03:23:56 +03:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " reconnect tcon rc = %d \n " , rc ) ;
2018-04-04 22:08:52 +03:00
if ( rc ) {
/* If sess reconnected but tcon didn't, something strange ... */
printk_once ( KERN_WARNING " reconnect tcon failed rc = %d \n " , rc ) ;
2011-12-27 16:23:34 +04:00
goto out ;
2018-04-04 22:08:52 +03:00
}
2016-11-29 22:31:23 +03:00
if ( smb2_command ! = SMB2_INTERNAL_CMD )
queue_delayed_work ( cifsiod_wq , & server - > reconnect , 0 ) ;
2011-12-27 16:23:34 +04:00
atomic_inc ( & tconInfoReconnectCount ) ;
out :
/*
* Check if handle based operation so we know whether we can continue
* or not without returning to caller to reset file handle .
*/
/*
* BB Is flush done by server on drop of tcp session ? Should we special
* case it and skip above ?
*/
switch ( smb2_command ) {
case SMB2_FLUSH :
case SMB2_READ :
case SMB2_WRITE :
case SMB2_LOCK :
case SMB2_IOCTL :
case SMB2_QUERY_DIRECTORY :
case SMB2_CHANGE_NOTIFY :
case SMB2_QUERY_INFO :
case SMB2_SET_INFO :
2016-11-29 22:30:58 +03:00
rc = - EAGAIN ;
2011-12-27 16:23:34 +04:00
}
unload_nls ( nls_codepage ) ;
2011-12-27 16:12:43 +04:00
return rc ;
}
2016-10-25 02:59:57 +03:00
static void
fill_small_buf ( __le16 smb2_command , struct cifs_tcon * tcon , void * buf ,
unsigned int * total_len )
{
struct smb2_sync_pdu * spdu = ( struct smb2_sync_pdu * ) buf ;
/* lookup word count ie StructureSize from table */
__u16 parmsize = smb2_req_struct_sizes [ le16_to_cpu ( smb2_command ) ] ;
/*
* smaller than SMALL_BUFFER_SIZE but bigger than fixed area of
* largest operations ( Create )
*/
memset ( buf , 0 , 256 ) ;
smb2_hdr_assemble ( & spdu - > sync_hdr , smb2_command , tcon ) ;
spdu - > StructureSize2 = cpu_to_le16 ( parmsize ) ;
* total_len = parmsize + sizeof ( struct smb2_sync_hdr ) ;
}
2011-12-27 16:12:43 +04:00
/*
* Allocate and return pointer to an SMB request hdr , and set basic
* SMB information in the SMB header . If the return code is zero , this
2017-11-21 03:04:42 +03:00
* function must have filled in request_buf pointer .
2011-12-27 16:12:43 +04:00
*/
static int
2017-11-21 03:04:42 +03:00
smb2_plain_req_init ( __le16 smb2_command , struct cifs_tcon * tcon ,
void * * request_buf , unsigned int * total_len )
2011-12-27 16:12:43 +04:00
{
2016-10-25 02:59:57 +03:00
int rc ;
2011-12-27 16:12:43 +04:00
rc = smb2_reconnect ( smb2_command , tcon ) ;
if ( rc )
return rc ;
/* BB eventually switch this to SMB2 specific small buf size */
2018-07-05 12:46:42 +03:00
if ( smb2_command = = SMB2_SET_INFO )
* request_buf = cifs_buf_get ( ) ;
else
* request_buf = cifs_small_buf_get ( ) ;
2011-12-27 16:12:43 +04:00
if ( * request_buf = = NULL ) {
/* BB should we add a retry in here if not a writepage? */
return - ENOMEM ;
}
2017-11-21 03:04:42 +03:00
fill_small_buf ( smb2_command , tcon ,
( struct smb2_sync_hdr * ) ( * request_buf ) ,
total_len ) ;
2011-12-27 16:12:43 +04:00
if ( tcon ! = NULL ) {
uint16_t com_code = le16_to_cpu ( smb2_command ) ;
cifs_stats_inc ( & tcon - > stats . smb2_stats . smb2_com_sent [ com_code ] ) ;
cifs_stats_inc ( & tcon - > num_smbs_sent ) ;
}
return rc ;
}
2018-06-29 03:30:23 +03:00
2015-06-18 12:49:47 +03:00
# define SMB2_PREAUTH_INTEGRITY_CAPABILITIES cpu_to_le16(1)
# define SMB2_ENCRYPTION_CAPABILITIES cpu_to_le16(2)
2018-05-20 04:45:27 +03:00
# define SMB2_POSIX_EXTENSIONS_AVAILABLE cpu_to_le16(0x100)
2015-06-18 12:49:47 +03:00
static void
build_preauth_ctxt ( struct smb2_preauth_neg_context * pneg_ctxt )
{
pneg_ctxt - > ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES ;
pneg_ctxt - > DataLength = cpu_to_le16 ( 38 ) ;
pneg_ctxt - > HashAlgorithmCount = cpu_to_le16 ( 1 ) ;
pneg_ctxt - > SaltLength = cpu_to_le16 ( SMB311_SALT_SIZE ) ;
get_random_bytes ( pneg_ctxt - > Salt , SMB311_SALT_SIZE ) ;
pneg_ctxt - > HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512 ;
}
static void
build_encrypt_ctxt ( struct smb2_encryption_neg_context * pneg_ctxt )
{
pneg_ctxt - > ContextType = SMB2_ENCRYPTION_CAPABILITIES ;
2018-04-22 23:14:58 +03:00
pneg_ctxt - > DataLength = cpu_to_le16 ( 4 ) ; /* Cipher Count + le16 cipher */
pneg_ctxt - > CipherCount = cpu_to_le16 ( 1 ) ;
/* pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM;*/ /* not supported yet */
pneg_ctxt - > Ciphers [ 0 ] = SMB2_ENCRYPTION_AES128_CCM ;
2015-06-18 12:49:47 +03:00
}
2018-05-20 04:45:27 +03:00
static void
build_posix_ctxt ( struct smb2_posix_neg_context * pneg_ctxt )
{
pneg_ctxt - > ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE ;
pneg_ctxt - > DataLength = cpu_to_le16 ( POSIX_CTXT_DATA_LEN ) ;
}
2015-06-18 12:49:47 +03:00
static void
2017-11-20 03:24:30 +03:00
assemble_neg_contexts ( struct smb2_negotiate_req * req ,
unsigned int * total_len )
2015-06-18 12:49:47 +03:00
{
2019-01-03 11:37:21 +03:00
char * pneg_ctxt = ( char * ) req ;
2018-05-20 04:45:27 +03:00
unsigned int ctxt_len ;
2015-06-18 12:49:47 +03:00
2019-01-03 11:37:21 +03:00
if ( * total_len > 200 ) {
/* In case length corrupted don't want to overrun smb buffer */
cifs_dbg ( VFS , " Bad frame length assembling neg contexts \n " ) ;
return ;
}
/*
* round up total_len of fixed part of SMB3 negotiate request to 8
* byte boundary before adding negotiate contexts
*/
* total_len = roundup ( * total_len , 8 ) ;
pneg_ctxt = ( * total_len ) + ( char * ) req ;
req - > NegotiateContextOffset = cpu_to_le32 ( * total_len ) ;
2015-06-18 12:49:47 +03:00
build_preauth_ctxt ( ( struct smb2_preauth_neg_context * ) pneg_ctxt ) ;
2018-05-20 04:45:27 +03:00
ctxt_len = DIV_ROUND_UP ( sizeof ( struct smb2_preauth_neg_context ) , 8 ) * 8 ;
* total_len + = ctxt_len ;
pneg_ctxt + = ctxt_len ;
2017-11-20 03:24:30 +03:00
2015-06-18 12:49:47 +03:00
build_encrypt_ctxt ( ( struct smb2_encryption_neg_context * ) pneg_ctxt ) ;
2018-05-20 04:45:27 +03:00
ctxt_len = DIV_ROUND_UP ( sizeof ( struct smb2_encryption_neg_context ) , 8 ) * 8 ;
* total_len + = ctxt_len ;
pneg_ctxt + = ctxt_len ;
build_posix_ctxt ( ( struct smb2_posix_neg_context * ) pneg_ctxt ) ;
* total_len + = sizeof ( struct smb2_posix_neg_context ) ;
2017-11-20 03:24:30 +03:00
2018-05-20 04:45:27 +03:00
req - > NegotiateContextCount = cpu_to_le16 ( 3 ) ;
2015-06-18 12:49:47 +03:00
}
2018-04-09 18:47:14 +03:00
static void decode_preauth_context ( struct smb2_preauth_neg_context * ctxt )
{
unsigned int len = le16_to_cpu ( ctxt - > DataLength ) ;
/* If invalid preauth context warn but use what we requested, SHA-512 */
if ( len < MIN_PREAUTH_CTXT_DATA_LEN ) {
printk_once ( KERN_WARNING " server sent bad preauth context \n " ) ;
return ;
}
if ( le16_to_cpu ( ctxt - > HashAlgorithmCount ) ! = 1 )
printk_once ( KERN_WARNING " illegal SMB3 hash algorithm count \n " ) ;
if ( ctxt - > HashAlgorithms ! = SMB2_PREAUTH_INTEGRITY_SHA512 )
printk_once ( KERN_WARNING " unknown SMB3 hash algorithm \n " ) ;
}
static int decode_encrypt_ctx ( struct TCP_Server_Info * server ,
struct smb2_encryption_neg_context * ctxt )
{
unsigned int len = le16_to_cpu ( ctxt - > DataLength ) ;
cifs_dbg ( FYI , " decode SMB3.11 encryption neg context of len %d \n " , len ) ;
if ( len < MIN_ENCRYPT_CTXT_DATA_LEN ) {
printk_once ( KERN_WARNING " server sent bad crypto ctxt len \n " ) ;
return - EINVAL ;
}
if ( le16_to_cpu ( ctxt - > CipherCount ) ! = 1 ) {
printk_once ( KERN_WARNING " illegal SMB3.11 cipher count \n " ) ;
return - EINVAL ;
}
cifs_dbg ( FYI , " SMB311 cipher type:%d \n " , le16_to_cpu ( ctxt - > Ciphers [ 0 ] ) ) ;
if ( ( ctxt - > Ciphers [ 0 ] ! = SMB2_ENCRYPTION_AES128_CCM ) & &
( ctxt - > Ciphers [ 0 ] ! = SMB2_ENCRYPTION_AES128_GCM ) ) {
printk_once ( KERN_WARNING " invalid SMB3.11 cipher returned \n " ) ;
return - EINVAL ;
}
server - > cipher_type = ctxt - > Ciphers [ 0 ] ;
2018-04-22 23:14:58 +03:00
server - > capabilities | = SMB2_GLOBAL_CAP_ENCRYPTION ;
2018-04-09 18:47:14 +03:00
return 0 ;
}
static int smb311_decode_neg_context ( struct smb2_negotiate_rsp * rsp ,
2018-06-01 03:53:02 +03:00
struct TCP_Server_Info * server ,
unsigned int len_of_smb )
2018-04-09 18:47:14 +03:00
{
struct smb2_neg_context * pctx ;
unsigned int offset = le32_to_cpu ( rsp - > NegotiateContextOffset ) ;
unsigned int ctxt_cnt = le16_to_cpu ( rsp - > NegotiateContextCount ) ;
unsigned int len_of_ctxts , i ;
int rc = 0 ;
cifs_dbg ( FYI , " decoding %d negotiate contexts \n " , ctxt_cnt ) ;
if ( len_of_smb < = offset ) {
cifs_dbg ( VFS , " Invalid response: negotiate context offset \n " ) ;
return - EINVAL ;
}
len_of_ctxts = len_of_smb - offset ;
for ( i = 0 ; i < ctxt_cnt ; i + + ) {
int clen ;
/* check that offset is not beyond end of SMB */
if ( len_of_ctxts = = 0 )
break ;
if ( len_of_ctxts < sizeof ( struct smb2_neg_context ) )
break ;
2018-06-01 03:53:07 +03:00
pctx = ( struct smb2_neg_context * ) ( offset + ( char * ) rsp ) ;
2018-04-09 18:47:14 +03:00
clen = le16_to_cpu ( pctx - > DataLength ) ;
if ( clen > len_of_ctxts )
break ;
if ( pctx - > ContextType = = SMB2_PREAUTH_INTEGRITY_CAPABILITIES )
decode_preauth_context (
( struct smb2_preauth_neg_context * ) pctx ) ;
else if ( pctx - > ContextType = = SMB2_ENCRYPTION_CAPABILITIES )
rc = decode_encrypt_ctx ( server ,
( struct smb2_encryption_neg_context * ) pctx ) ;
2018-05-20 04:45:27 +03:00
else if ( pctx - > ContextType = = SMB2_POSIX_EXTENSIONS_AVAILABLE )
server - > posix_ext_supported = true ;
2018-04-09 18:47:14 +03:00
else
cifs_dbg ( VFS , " unknown negcontext of type %d ignored \n " ,
le16_to_cpu ( pctx - > ContextType ) ) ;
if ( rc )
break ;
/* offsets must be 8 byte aligned */
clen = ( clen + 7 ) & ~ 0x7 ;
offset + = clen + sizeof ( struct smb2_neg_context ) ;
len_of_ctxts - = clen ;
}
return rc ;
}
2018-06-01 03:16:54 +03:00
static struct create_posix *
create_posix_buf ( umode_t mode )
{
struct create_posix * buf ;
buf = kzalloc ( sizeof ( struct create_posix ) ,
GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
buf - > ccontext . DataOffset =
cpu_to_le16 ( offsetof ( struct create_posix , Mode ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( 4 ) ;
buf - > ccontext . NameOffset =
cpu_to_le16 ( offsetof ( struct create_posix , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 16 ) ;
/* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */
buf - > Name [ 0 ] = 0x93 ;
buf - > Name [ 1 ] = 0xAD ;
buf - > Name [ 2 ] = 0x25 ;
buf - > Name [ 3 ] = 0x50 ;
buf - > Name [ 4 ] = 0x9C ;
buf - > Name [ 5 ] = 0xB4 ;
buf - > Name [ 6 ] = 0x11 ;
buf - > Name [ 7 ] = 0xE7 ;
buf - > Name [ 8 ] = 0xB4 ;
buf - > Name [ 9 ] = 0x23 ;
buf - > Name [ 10 ] = 0x83 ;
buf - > Name [ 11 ] = 0xDE ;
buf - > Name [ 12 ] = 0x96 ;
buf - > Name [ 13 ] = 0x8B ;
buf - > Name [ 14 ] = 0xCD ;
buf - > Name [ 15 ] = 0x7C ;
buf - > Mode = cpu_to_le32 ( mode ) ;
cifs_dbg ( FYI , " mode on posix create 0%o " , mode ) ;
return buf ;
}
static int
add_posix_context ( struct kvec * iov , unsigned int * num_iovec , umode_t mode )
{
struct smb2_create_req * req = iov [ 0 ] . iov_base ;
unsigned int num = * num_iovec ;
iov [ num ] . iov_base = create_posix_buf ( mode ) ;
if ( iov [ num ] . iov_base = = NULL )
return - ENOMEM ;
iov [ num ] . iov_len = sizeof ( struct create_posix ) ;
if ( ! req - > CreateContextsOffset )
req - > CreateContextsOffset = cpu_to_le32 (
sizeof ( struct smb2_create_req ) +
iov [ num - 1 ] . iov_len ) ;
le32_add_cpu ( & req - > CreateContextsLength , sizeof ( struct create_posix ) ) ;
* num_iovec = num + 1 ;
return 0 ;
}
2015-06-18 12:49:47 +03:00
2011-12-27 16:12:43 +04:00
/*
*
* SMB2 Worker functions follow :
*
* The general structure of the worker functions is :
* 1 ) Call smb2_init ( assembles SMB2 header )
* 2 ) Initialize SMB2 command specific fields in fixed length area of SMB
* 3 ) Call smb_sendrcv2 ( sends request on socket and waits for response )
* 4 ) Decode SMB2 command specific fields in the fixed length area
* 5 ) Decode variable length data area ( if any for this SMB2 command type )
* 6 ) Call free smb buffer
* 7 ) return
*
*/
int
SMB2_negotiate ( const unsigned int xid , struct cifs_ses * ses )
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2011-12-27 16:12:43 +04:00
struct smb2_negotiate_req * req ;
struct smb2_negotiate_rsp * rsp ;
struct kvec iov [ 1 ] ;
2016-10-25 21:38:47 +03:00
struct kvec rsp_iov ;
2011-12-27 16:12:43 +04:00
int rc = 0 ;
int resp_buftype ;
2013-05-24 15:41:01 +04:00
struct TCP_Server_Info * server = ses - > server ;
2011-12-27 16:12:43 +04:00
int blob_offset , blob_length ;
char * security_blob ;
int flags = CIFS_NEG_OP ;
2017-11-20 03:24:30 +03:00
unsigned int total_len ;
2011-12-27 16:12:43 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Negotiate protocol \n " ) ;
2011-12-27 16:12:43 +04:00
2013-05-24 15:41:01 +04:00
if ( ! server ) {
WARN ( 1 , " %s: server is NULL! \n " , __func__ ) ;
return - EIO ;
2011-12-27 16:12:43 +04:00
}
2017-11-20 03:24:30 +03:00
rc = smb2_plain_req_init ( SMB2_NEGOTIATE , NULL , ( void * * ) & req , & total_len ) ;
2011-12-27 16:12:43 +04:00
if ( rc )
return rc ;
2017-11-20 03:24:30 +03:00
req - > sync_hdr . SessionId = 0 ;
2018-06-29 03:30:23 +03:00
2018-02-16 21:19:29 +03:00
memset ( server - > preauth_sha_hash , 0 , SMB2_PREAUTH_HASH_SIZE ) ;
memset ( ses - > preauth_sha_hash , 0 , SMB2_PREAUTH_HASH_SIZE ) ;
2011-12-27 16:12:43 +04:00
2017-09-17 18:41:35 +03:00
if ( strcmp ( ses - > server - > vals - > version_string ,
SMB3ANY_VERSION_STRING ) = = 0 ) {
req - > Dialects [ 0 ] = cpu_to_le16 ( SMB30_PROT_ID ) ;
req - > Dialects [ 1 ] = cpu_to_le16 ( SMB302_PROT_ID ) ;
req - > DialectCount = cpu_to_le16 ( 2 ) ;
2017-11-20 03:24:30 +03:00
total_len + = 4 ;
2017-09-17 18:41:35 +03:00
} else if ( strcmp ( ses - > server - > vals - > version_string ,
SMBDEFAULT_VERSION_STRING ) = = 0 ) {
req - > Dialects [ 0 ] = cpu_to_le16 ( SMB21_PROT_ID ) ;
req - > Dialects [ 1 ] = cpu_to_le16 ( SMB30_PROT_ID ) ;
req - > Dialects [ 2 ] = cpu_to_le16 ( SMB302_PROT_ID ) ;
2019-01-03 11:37:21 +03:00
req - > Dialects [ 3 ] = cpu_to_le16 ( SMB311_PROT_ID ) ;
req - > DialectCount = cpu_to_le16 ( 4 ) ;
total_len + = 8 ;
2017-09-17 18:41:35 +03:00
} else {
/* otherwise send specific dialect */
req - > Dialects [ 0 ] = cpu_to_le16 ( ses - > server - > vals - > protocol_id ) ;
req - > DialectCount = cpu_to_le16 ( 1 ) ;
2017-11-20 03:24:30 +03:00
total_len + = 2 ;
2017-09-17 18:41:35 +03:00
}
2011-12-27 16:12:43 +04:00
/* only one of SMB2 signing flags may be set in SMB2 request */
2013-05-26 15:01:00 +04:00
if ( ses - > sign )
2013-06-13 04:59:03 +04:00
req - > SecurityMode = cpu_to_le16 ( SMB2_NEGOTIATE_SIGNING_REQUIRED ) ;
2013-05-26 15:01:00 +04:00
else if ( global_secflags & CIFSSEC_MAY_SIGN )
2013-06-13 04:59:03 +04:00
req - > SecurityMode = cpu_to_le16 ( SMB2_NEGOTIATE_SIGNING_ENABLED ) ;
2013-05-26 15:01:00 +04:00
else
req - > SecurityMode = 0 ;
2011-12-27 16:12:43 +04:00
2012-10-01 21:26:22 +04:00
req - > Capabilities = cpu_to_le32 ( ses - > server - > vals - > req_capabilities ) ;
2011-12-27 16:12:43 +04:00
2014-05-14 00:37:45 +04:00
/* ClientGUID must be zero for SMB2.02 dialect */
if ( ses - > server - > vals - > protocol_id = = SMB20_PROT_ID )
memset ( req - > ClientGUID , 0 , SMB2_CLIENT_GUID_SIZE ) ;
2015-06-18 12:49:47 +03:00
else {
2014-05-14 00:37:45 +04:00
memcpy ( req - > ClientGUID , server - > client_guid ,
SMB2_CLIENT_GUID_SIZE ) ;
2019-01-03 11:37:21 +03:00
if ( ( ses - > server - > vals - > protocol_id = = SMB311_PROT_ID ) | |
( strcmp ( ses - > server - > vals - > version_string ,
SMBDEFAULT_VERSION_STRING ) = = 0 ) )
2017-11-20 03:24:30 +03:00
assemble_neg_contexts ( req , & total_len ) ;
2015-06-18 12:49:47 +03:00
}
2011-12-27 16:12:43 +04:00
iov [ 0 ] . iov_base = ( char * ) req ;
2017-11-20 03:24:30 +03:00
iov [ 0 ] . iov_len = total_len ;
2011-12-27 16:12:43 +04:00
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = 1 ;
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( req ) ;
rsp = ( struct smb2_negotiate_rsp * ) rsp_iov . iov_base ;
2011-12-27 16:12:43 +04:00
/*
* No tcon so can ' t do
* cifs_stats_inc ( & tcon - > stats . smb2_stats . smb2_com_fail [ SMB2 . . . ] ) ;
*/
2017-09-01 05:34:24 +03:00
if ( rc = = - EOPNOTSUPP ) {
cifs_dbg ( VFS , " Dialect not supported by server. Consider "
2017-09-17 18:41:35 +03:00
" specifying vers=1.0 or vers=2.0 on mount for accessing "
2017-09-01 05:34:24 +03:00
" older servers \n " ) ;
goto neg_exit ;
} else if ( rc ! = 0 )
2011-12-27 16:12:43 +04:00
goto neg_exit ;
2017-09-17 18:41:35 +03:00
if ( strcmp ( ses - > server - > vals - > version_string ,
SMB3ANY_VERSION_STRING ) = = 0 ) {
if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB20_PROT_ID ) ) {
cifs_dbg ( VFS ,
" SMB2 dialect returned but not requested \n " ) ;
return - EIO ;
} else if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB21_PROT_ID ) ) {
cifs_dbg ( VFS ,
" SMB2.1 dialect returned but not requested \n " ) ;
return - EIO ;
}
} else if ( strcmp ( ses - > server - > vals - > version_string ,
SMBDEFAULT_VERSION_STRING ) = = 0 ) {
if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB20_PROT_ID ) ) {
cifs_dbg ( VFS ,
" SMB2 dialect returned but not requested \n " ) ;
return - EIO ;
} else if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB21_PROT_ID ) ) {
/* ops set to 3.0 by default for default so update */
ses - > server - > ops = & smb21_operations ;
2019-01-03 11:37:21 +03:00
} else if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB311_PROT_ID ) )
ses - > server - > ops = & smb311_operations ;
2017-09-19 19:43:47 +03:00
} else if ( le16_to_cpu ( rsp - > DialectRevision ) ! =
ses - > server - > vals - > protocol_id ) {
2017-09-17 18:41:35 +03:00
/* if requested single dialect ensure returned dialect matched */
cifs_dbg ( VFS , " Illegal 0x%x dialect returned: not requested \n " ,
2017-09-19 19:43:47 +03:00
le16_to_cpu ( rsp - > DialectRevision ) ) ;
2017-09-17 18:41:35 +03:00
return - EIO ;
}
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " mode 0x%x \n " , rsp - > SecurityMode ) ;
2011-12-27 16:12:43 +04:00
2012-10-01 21:26:22 +04:00
if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB20_PROT_ID ) )
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " negotiated smb2.0 dialect \n " ) ;
2012-10-01 21:26:22 +04:00
else if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB21_PROT_ID ) )
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " negotiated smb2.1 dialect \n " ) ;
2012-10-01 21:26:22 +04:00
else if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB30_PROT_ID ) )
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " negotiated smb3.0 dialect \n " ) ;
2013-06-13 07:48:41 +04:00
else if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB302_PROT_ID ) )
cifs_dbg ( FYI , " negotiated smb3.02 dialect \n " ) ;
2014-12-18 07:52:58 +03:00
else if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB311_PROT_ID ) )
cifs_dbg ( FYI , " negotiated smb3.1.1 dialect \n " ) ;
2011-12-27 16:12:43 +04:00
else {
2015-06-18 13:07:52 +03:00
cifs_dbg ( VFS , " Illegal dialect returned by server 0x%x \n " ,
2013-05-05 07:12:25 +04:00
le16_to_cpu ( rsp - > DialectRevision ) ) ;
2011-12-27 16:12:43 +04:00
rc = - EIO ;
goto neg_exit ;
}
server - > dialect = le16_to_cpu ( rsp - > DialectRevision ) ;
2018-02-16 21:19:29 +03:00
/*
* Keep a copy of the hash after negprot . This hash will be
* the starting hash value for all sessions made from this
* server .
*/
memcpy ( server - > preauth_sha_hash , ses - > preauth_sha_hash ,
SMB2_PREAUTH_HASH_SIZE ) ;
2018-06-29 03:30:23 +03:00
2013-05-26 15:00:59 +04:00
/* SMB2 only has an extended negflavor */
server - > negflavor = CIFS_NEGFLAVOR_EXTENDED ;
2014-02-14 13:31:02 +04:00
/* set it to the maximum buffer size value we can send with 1 credit */
server - > maxBuf = min_t ( unsigned int , le32_to_cpu ( rsp - > MaxTransactSize ) ,
SMB2_MAX_BUFFER_SIZE ) ;
2011-12-27 16:12:43 +04:00
server - > max_read = le32_to_cpu ( rsp - > MaxReadSize ) ;
server - > max_write = le32_to_cpu ( rsp - > MaxWriteSize ) ;
server - > sec_mode = le16_to_cpu ( rsp - > SecurityMode ) ;
2018-04-02 04:15:55 +03:00
if ( ( server - > sec_mode & SMB2_SEC_MODE_FLAGS_ALL ) ! = server - > sec_mode )
cifs_dbg ( FYI , " Server returned unexpected security mode 0x%x \n " ,
server - > sec_mode ) ;
2011-12-27 16:12:43 +04:00
server - > capabilities = le32_to_cpu ( rsp - > Capabilities ) ;
2012-07-13 13:58:14 +04:00
/* Internal types */
server - > capabilities | = SMB2_NT_FIND | SMB2_LARGE_FILES ;
2011-12-27 16:12:43 +04:00
security_blob = smb2_get_data_area_len ( & blob_offset , & blob_length ,
2018-06-01 03:53:06 +03:00
( struct smb2_sync_hdr * ) rsp ) ;
2013-06-26 00:33:41 +04:00
/*
* See MS - SMB2 section 2.2 .4 : if no blob , client picks default which
* for us will be
* ses - > sectype = RawNTLMSSP ;
* but for time being this is our only auth choice so doesn ' t matter .
* We just found a server which sets blob length to zero expecting raw .
*/
2017-04-12 23:32:07 +03:00
if ( blob_length = = 0 ) {
2013-06-26 00:33:41 +04:00
cifs_dbg ( FYI , " missing security blob on negprot \n " ) ;
2017-04-12 23:32:07 +03:00
server - > sec_ntlmssp = true ;
}
2012-09-19 03:20:30 +04:00
2013-05-26 15:01:00 +04:00
rc = cifs_enable_signing ( server , ses - > sign ) ;
2013-05-26 15:00:58 +04:00
if ( rc )
goto neg_exit ;
2015-09-24 08:52:37 +03:00
if ( blob_length ) {
2014-10-20 21:48:23 +04:00
rc = decode_negTokenInit ( security_blob , blob_length , server ) ;
2015-09-24 08:52:37 +03:00
if ( rc = = 1 )
rc = 0 ;
else if ( rc = = 0 )
rc = - EIO ;
2011-12-27 16:12:43 +04:00
}
2018-04-09 18:47:14 +03:00
if ( rsp - > DialectRevision = = cpu_to_le16 ( SMB311_PROT_ID ) ) {
if ( rsp - > NegotiateContextCount )
2018-06-01 03:53:02 +03:00
rc = smb311_decode_neg_context ( rsp , server ,
rsp_iov . iov_len ) ;
2018-04-09 18:47:14 +03:00
else
cifs_dbg ( VFS , " Missing expected negotiate contexts \n " ) ;
}
2011-12-27 16:12:43 +04:00
neg_exit :
free_rsp_buf ( resp_buftype , rsp ) ;
return rc ;
}
2011-12-27 16:22:00 +04:00
2013-11-20 09:44:46 +04:00
int smb3_validate_negotiate ( const unsigned int xid , struct cifs_tcon * tcon )
{
2018-04-25 21:30:04 +03:00
int rc ;
struct validate_negotiate_info_req * pneg_inbuf ;
2017-10-20 15:49:37 +03:00
struct validate_negotiate_info_rsp * pneg_rsp = NULL ;
2013-11-20 09:44:46 +04:00
u32 rsplen ;
2017-09-17 18:41:35 +03:00
u32 inbuflen ; /* max of 4 dialects */
2013-11-20 09:44:46 +04:00
cifs_dbg ( FYI , " validate negotiate \n " ) ;
2018-02-16 21:19:29 +03:00
/* In SMB3.11 preauth integrity supersedes validate negotiate */
if ( tcon - > ses - > server - > dialect = = SMB311_PROT_ID )
return 0 ;
2013-11-20 09:44:46 +04:00
/*
* validation ioctl must be signed , so no point sending this if we
2017-09-21 03:57:18 +03:00
* can not sign it ( ie are not known user ) . Even if signing is not
* required ( enabled but not negotiated ) , in those cases we selectively
2013-11-20 09:44:46 +04:00
* sign just this , the first and only signed request on a connection .
2017-09-21 03:57:18 +03:00
* Having validation of negotiate info helps reduce attack vectors .
2013-11-20 09:44:46 +04:00
*/
2017-09-21 03:57:18 +03:00
if ( tcon - > ses - > session_flags & SMB2_SESSION_FLAG_IS_GUEST )
2013-11-20 09:44:46 +04:00
return 0 ; /* validation requires signing */
2017-09-21 03:57:18 +03:00
if ( tcon - > ses - > user_name = = NULL ) {
cifs_dbg ( FYI , " Can't validate negotiate: null user mount \n " ) ;
return 0 ; /* validation requires signing */
}
if ( tcon - > ses - > session_flags & SMB2_SESSION_FLAG_IS_NULL )
cifs_dbg ( VFS , " Unexpected null user (anonymous) auth flag sent by server \n " ) ;
2018-04-25 21:30:04 +03:00
pneg_inbuf = kmalloc ( sizeof ( * pneg_inbuf ) , GFP_NOFS ) ;
if ( ! pneg_inbuf )
return - ENOMEM ;
pneg_inbuf - > Capabilities =
2013-11-20 09:44:46 +04:00
cpu_to_le32 ( tcon - > ses - > server - > vals - > req_capabilities ) ;
2018-04-25 21:30:04 +03:00
memcpy ( pneg_inbuf - > Guid , tcon - > ses - > server - > client_guid ,
2014-05-13 03:48:12 +04:00
SMB2_CLIENT_GUID_SIZE ) ;
2013-11-20 09:44:46 +04:00
if ( tcon - > ses - > sign )
2018-04-25 21:30:04 +03:00
pneg_inbuf - > SecurityMode =
2013-11-20 09:44:46 +04:00
cpu_to_le16 ( SMB2_NEGOTIATE_SIGNING_REQUIRED ) ;
else if ( global_secflags & CIFSSEC_MAY_SIGN )
2018-04-25 21:30:04 +03:00
pneg_inbuf - > SecurityMode =
2013-11-20 09:44:46 +04:00
cpu_to_le16 ( SMB2_NEGOTIATE_SIGNING_ENABLED ) ;
else
2018-04-25 21:30:04 +03:00
pneg_inbuf - > SecurityMode = 0 ;
2013-11-20 09:44:46 +04:00
2017-09-17 18:41:35 +03:00
if ( strcmp ( tcon - > ses - > server - > vals - > version_string ,
SMB3ANY_VERSION_STRING ) = = 0 ) {
2018-04-25 21:30:04 +03:00
pneg_inbuf - > Dialects [ 0 ] = cpu_to_le16 ( SMB30_PROT_ID ) ;
pneg_inbuf - > Dialects [ 1 ] = cpu_to_le16 ( SMB302_PROT_ID ) ;
pneg_inbuf - > DialectCount = cpu_to_le16 ( 2 ) ;
2017-09-17 18:41:35 +03:00
/* structure is big enough for 3 dialects, sending only 2 */
2018-04-25 21:30:04 +03:00
inbuflen = sizeof ( * pneg_inbuf ) -
2019-01-03 11:37:21 +03:00
( 2 * sizeof ( pneg_inbuf - > Dialects [ 0 ] ) ) ;
2017-09-17 18:41:35 +03:00
} else if ( strcmp ( tcon - > ses - > server - > vals - > version_string ,
SMBDEFAULT_VERSION_STRING ) = = 0 ) {
2018-04-25 21:30:04 +03:00
pneg_inbuf - > Dialects [ 0 ] = cpu_to_le16 ( SMB21_PROT_ID ) ;
pneg_inbuf - > Dialects [ 1 ] = cpu_to_le16 ( SMB30_PROT_ID ) ;
pneg_inbuf - > Dialects [ 2 ] = cpu_to_le16 ( SMB302_PROT_ID ) ;
2019-01-03 11:37:21 +03:00
pneg_inbuf - > Dialects [ 3 ] = cpu_to_le16 ( SMB311_PROT_ID ) ;
pneg_inbuf - > DialectCount = cpu_to_le16 ( 4 ) ;
2017-09-17 18:41:35 +03:00
/* structure is big enough for 3 dialects */
2018-04-25 21:30:04 +03:00
inbuflen = sizeof ( * pneg_inbuf ) ;
2017-09-17 18:41:35 +03:00
} else {
/* otherwise specific dialect was requested */
2018-04-25 21:30:04 +03:00
pneg_inbuf - > Dialects [ 0 ] =
2017-09-17 18:41:35 +03:00
cpu_to_le16 ( tcon - > ses - > server - > vals - > protocol_id ) ;
2018-04-25 21:30:04 +03:00
pneg_inbuf - > DialectCount = cpu_to_le16 ( 1 ) ;
2017-09-17 18:41:35 +03:00
/* structure is big enough for 3 dialects, sending only 1 */
2018-04-25 21:30:04 +03:00
inbuflen = sizeof ( * pneg_inbuf ) -
sizeof ( pneg_inbuf - > Dialects [ 0 ] ) * 2 ;
2017-09-17 18:41:35 +03:00
}
2013-11-20 09:44:46 +04:00
rc = SMB2_ioctl ( xid , tcon , NO_FILE_ID , NO_FILE_ID ,
FSCTL_VALIDATE_NEGOTIATE_INFO , true /* is_fsctl */ ,
2018-04-25 21:30:04 +03:00
( char * ) pneg_inbuf , inbuflen , ( char * * ) & pneg_rsp , & rsplen ) ;
2013-11-20 09:44:46 +04:00
if ( rc ! = 0 ) {
cifs_dbg ( VFS , " validate protocol negotiate failed: %d \n " , rc ) ;
2018-04-25 21:30:04 +03:00
rc = - EIO ;
goto out_free_inbuf ;
2013-11-20 09:44:46 +04:00
}
2018-04-25 21:30:04 +03:00
rc = - EIO ;
if ( rsplen ! = sizeof ( * pneg_rsp ) ) {
2017-05-04 05:12:20 +03:00
cifs_dbg ( VFS , " invalid protocol negotiate response size: %d \n " ,
rsplen ) ;
/* relax check since Mac returns max bufsize allowed on ioctl */
2018-04-25 21:30:04 +03:00
if ( rsplen > CIFSMaxBufSize | | rsplen < sizeof ( * pneg_rsp ) )
goto out_free_rsp ;
2013-11-20 09:44:46 +04:00
}
/* check validate negotiate info response matches what we got earlier */
2018-01-11 18:00:12 +03:00
if ( pneg_rsp - > Dialect ! = cpu_to_le16 ( tcon - > ses - > server - > dialect ) )
2013-11-20 09:44:46 +04:00
goto vneg_out ;
if ( pneg_rsp - > SecurityMode ! = cpu_to_le16 ( tcon - > ses - > server - > sec_mode ) )
goto vneg_out ;
/* do not validate server guid because not saved at negprot time yet */
if ( ( le32_to_cpu ( pneg_rsp - > Capabilities ) | SMB2_NT_FIND |
SMB2_LARGE_FILES ) ! = tcon - > ses - > server - > capabilities )
goto vneg_out ;
/* validate negotiate successful */
2018-04-25 21:30:04 +03:00
rc = 0 ;
2013-11-20 09:44:46 +04:00
cifs_dbg ( FYI , " validate negotiate info successful \n " ) ;
2018-04-25 21:30:04 +03:00
goto out_free_rsp ;
2013-11-20 09:44:46 +04:00
vneg_out :
cifs_dbg ( VFS , " protocol revalidation - security settings mismatch \n " ) ;
2018-04-25 21:30:04 +03:00
out_free_rsp :
2017-10-20 15:49:37 +03:00
kfree ( pneg_rsp ) ;
2018-04-25 21:30:04 +03:00
out_free_inbuf :
kfree ( pneg_inbuf ) ;
return rc ;
2013-11-20 09:44:46 +04:00
}
2017-01-18 13:05:57 +03:00
enum securityEnum
smb2_select_sectype ( struct TCP_Server_Info * server , enum securityEnum requested )
{
switch ( requested ) {
case Kerberos :
case RawNTLMSSP :
return requested ;
case NTLMv2 :
return RawNTLMSSP ;
case Unspecified :
if ( server - > sec_ntlmssp & &
( global_secflags & CIFSSEC_MAY_NTLMSSP ) )
return RawNTLMSSP ;
if ( ( server - > sec_kerberos | | server - > sec_mskerberos ) & &
( global_secflags & CIFSSEC_MAY_KRB5 ) )
return Kerberos ;
/* Fallthrough */
default :
return Unspecified ;
}
}
2016-10-07 21:11:21 +03:00
struct SMB2_sess_data {
unsigned int xid ;
struct cifs_ses * ses ;
struct nls_table * nls_cp ;
void ( * func ) ( struct SMB2_sess_data * ) ;
int result ;
u64 previous_session ;
/* we will send the SMB in three pieces:
* a fixed length beginning part , an optional
* SPNEGO blob ( which can be zero length ) , and a
* last part which will include the strings
* and rest of bcc area . This allows us to avoid
* a large buffer 17 K allocation
*/
int buf0_type ;
struct kvec iov [ 2 ] ;
} ;
static int
SMB2_sess_alloc_buffer ( struct SMB2_sess_data * sess_data )
{
int rc ;
struct cifs_ses * ses = sess_data - > ses ;
struct smb2_sess_setup_req * req ;
struct TCP_Server_Info * server = ses - > server ;
2017-11-20 03:24:36 +03:00
unsigned int total_len ;
2016-10-07 21:11:21 +03:00
2017-11-20 03:24:36 +03:00
rc = smb2_plain_req_init ( SMB2_SESSION_SETUP , NULL , ( void * * ) & req ,
& total_len ) ;
2016-10-07 21:11:21 +03:00
if ( rc )
return rc ;
2016-10-25 01:33:04 +03:00
/* First session, not a reauthenticate */
2017-11-20 03:24:36 +03:00
req - > sync_hdr . SessionId = 0 ;
2016-10-07 21:11:21 +03:00
/* if reconnect, we need to send previous sess id, otherwise it is 0 */
req - > PreviousSessionId = sess_data - > previous_session ;
req - > Flags = 0 ; /* MBZ */
2018-06-14 01:05:58 +03:00
/* enough to enable echos and oplocks and one max size write */
req - > sync_hdr . CreditRequest = cpu_to_le16 ( 130 ) ;
2016-10-07 21:11:21 +03:00
/* only one of SMB2 signing flags may be set in SMB2 request */
if ( server - > sign )
req - > SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED ;
else if ( global_secflags & CIFSSEC_MAY_SIGN ) /* one flag unlike MUST_ */
req - > SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED ;
else
req - > SecurityMode = 0 ;
req - > Capabilities = 0 ;
req - > Channel = 0 ; /* MBZ */
sess_data - > iov [ 0 ] . iov_base = ( char * ) req ;
2017-11-20 03:24:36 +03:00
/* 1 for pad */
sess_data - > iov [ 0 ] . iov_len = total_len - 1 ;
2016-10-07 21:11:21 +03:00
/*
* This variable will be used to clear the buffer
* allocated above in case of any error in the calling function .
*/
sess_data - > buf0_type = CIFS_SMALL_BUFFER ;
return 0 ;
}
static void
SMB2_sess_free_buffer ( struct SMB2_sess_data * sess_data )
{
free_rsp_buf ( sess_data - > buf0_type , sess_data - > iov [ 0 ] . iov_base ) ;
sess_data - > buf0_type = CIFS_NO_BUFFER ;
}
static int
SMB2_sess_sendreceive ( struct SMB2_sess_data * sess_data )
{
int rc ;
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2016-10-07 21:11:21 +03:00
struct smb2_sess_setup_req * req = sess_data - > iov [ 0 ] . iov_base ;
2016-10-25 21:38:47 +03:00
struct kvec rsp_iov = { NULL , 0 } ;
2016-10-07 21:11:21 +03:00
/* Testing shows that buffer offset must be at location of Buffer[0] */
req - > SecurityBufferOffset =
2017-11-20 03:24:36 +03:00
cpu_to_le16 ( sizeof ( struct smb2_sess_setup_req ) - 1 /* pad */ ) ;
2016-10-07 21:11:21 +03:00
req - > SecurityBufferLength = cpu_to_le16 ( sess_data - > iov [ 1 ] . iov_len ) ;
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = sess_data - > iov ;
rqst . rq_nvec = 2 ;
2016-10-07 21:11:21 +03:00
2018-06-12 01:00:59 +03:00
/* BB add code to build os and lm fields */
rc = cifs_send_recv ( sess_data - > xid , sess_data - > ses ,
& rqst ,
2017-11-20 03:24:36 +03:00
& sess_data - > buf0_type ,
CIFS_LOG_ERROR | CIFS_NEG_OP , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( sess_data - > iov [ 0 ] . iov_base ) ;
memcpy ( & sess_data - > iov [ 0 ] , & rsp_iov , sizeof ( struct kvec ) ) ;
2016-10-07 21:11:21 +03:00
return rc ;
}
static int
SMB2_sess_establish_session ( struct SMB2_sess_data * sess_data )
{
int rc = 0 ;
struct cifs_ses * ses = sess_data - > ses ;
mutex_lock ( & ses - > server - > srv_mutex ) ;
2016-11-08 05:20:50 +03:00
if ( ses - > server - > ops - > generate_signingkey ) {
2016-10-07 21:11:21 +03:00
rc = ses - > server - > ops - > generate_signingkey ( ses ) ;
if ( rc ) {
cifs_dbg ( FYI ,
" SMB3 session key generation failed \n " ) ;
mutex_unlock ( & ses - > server - > srv_mutex ) ;
2016-11-08 05:20:50 +03:00
return rc ;
2016-10-07 21:11:21 +03:00
}
}
if ( ! ses - > server - > session_estab ) {
ses - > server - > sequence_number = 0x2 ;
ses - > server - > session_estab = true ;
}
mutex_unlock ( & ses - > server - > srv_mutex ) ;
cifs_dbg ( FYI , " SMB2/3 session established successfully \n " ) ;
spin_lock ( & GlobalMid_Lock ) ;
ses - > status = CifsGood ;
ses - > need_reconnect = false ;
spin_unlock ( & GlobalMid_Lock ) ;
return rc ;
}
# ifdef CONFIG_CIFS_UPCALL
static void
SMB2_auth_kerberos ( struct SMB2_sess_data * sess_data )
{
int rc ;
struct cifs_ses * ses = sess_data - > ses ;
struct cifs_spnego_msg * msg ;
struct key * spnego_key = NULL ;
struct smb2_sess_setup_rsp * rsp = NULL ;
rc = SMB2_sess_alloc_buffer ( sess_data ) ;
if ( rc )
goto out ;
spnego_key = cifs_get_spnego_key ( ses ) ;
if ( IS_ERR ( spnego_key ) ) {
rc = PTR_ERR ( spnego_key ) ;
spnego_key = NULL ;
goto out ;
}
msg = spnego_key - > payload . data [ 0 ] ;
/*
* check version field to make sure that cifs . upcall is
* sending us a response in an expected form
*/
if ( msg - > version ! = CIFS_SPNEGO_UPCALL_VERSION ) {
cifs_dbg ( VFS ,
" bad cifs.upcall version. Expected %d got %d " ,
CIFS_SPNEGO_UPCALL_VERSION , msg - > version ) ;
rc = - EKEYREJECTED ;
goto out_put_spnego_key ;
}
ses - > auth_key . response = kmemdup ( msg - > data , msg - > sesskey_len ,
GFP_KERNEL ) ;
if ( ! ses - > auth_key . response ) {
cifs_dbg ( VFS ,
" Kerberos can't allocate (%u bytes) memory " ,
msg - > sesskey_len ) ;
rc = - ENOMEM ;
goto out_put_spnego_key ;
}
ses - > auth_key . len = msg - > sesskey_len ;
sess_data - > iov [ 1 ] . iov_base = msg - > data + msg - > sesskey_len ;
sess_data - > iov [ 1 ] . iov_len = msg - > secblob_len ;
rc = SMB2_sess_sendreceive ( sess_data ) ;
if ( rc )
goto out_put_spnego_key ;
rsp = ( struct smb2_sess_setup_rsp * ) sess_data - > iov [ 0 ] . iov_base ;
2018-06-01 03:53:06 +03:00
ses - > Suid = rsp - > sync_hdr . SessionId ;
2016-10-07 21:11:21 +03:00
ses - > session_flags = le16_to_cpu ( rsp - > SessionFlags ) ;
rc = SMB2_sess_establish_session ( sess_data ) ;
out_put_spnego_key :
key_invalidate ( spnego_key ) ;
key_put ( spnego_key ) ;
out :
sess_data - > result = rc ;
sess_data - > func = NULL ;
SMB2_sess_free_buffer ( sess_data ) ;
}
# else
static void
SMB2_auth_kerberos ( struct SMB2_sess_data * sess_data )
{
cifs_dbg ( VFS , " Kerberos negotiated but upcall support disabled! \n " ) ;
sess_data - > result = - EOPNOTSUPP ;
sess_data - > func = NULL ;
}
# endif
2016-10-07 21:11:22 +03:00
static void
SMB2_sess_auth_rawntlmssp_authenticate ( struct SMB2_sess_data * sess_data ) ;
static void
SMB2_sess_auth_rawntlmssp_negotiate ( struct SMB2_sess_data * sess_data )
2011-12-27 16:22:00 +04:00
{
2016-10-07 21:11:22 +03:00
int rc ;
struct cifs_ses * ses = sess_data - > ses ;
2011-12-27 16:22:00 +04:00
struct smb2_sess_setup_rsp * rsp = NULL ;
2016-10-07 21:11:22 +03:00
char * ntlmssp_blob = NULL ;
2011-12-27 16:22:00 +04:00
bool use_spnego = false ; /* else use raw ntlmssp */
2016-10-07 21:11:22 +03:00
u16 blob_length = 0 ;
2013-08-29 17:35:09 +04:00
2011-12-27 16:22:00 +04:00
/*
* If memory allocation is successful , caller of this function
* frees it .
*/
ses - > ntlmssp = kmalloc ( sizeof ( struct ntlmssp_auth ) , GFP_KERNEL ) ;
2016-10-07 21:11:22 +03:00
if ( ! ses - > ntlmssp ) {
rc = - ENOMEM ;
goto out_err ;
}
2013-08-29 17:35:10 +04:00
ses - > ntlmssp - > sesskey_per_smbsess = true ;
2011-12-27 16:22:00 +04:00
2016-10-07 21:11:22 +03:00
rc = SMB2_sess_alloc_buffer ( sess_data ) ;
2011-12-27 16:22:00 +04:00
if ( rc )
2016-10-07 21:11:22 +03:00
goto out_err ;
2011-12-27 16:22:00 +04:00
2016-10-07 21:11:22 +03:00
ntlmssp_blob = kmalloc ( sizeof ( struct _NEGOTIATE_MESSAGE ) ,
GFP_KERNEL ) ;
if ( ntlmssp_blob = = NULL ) {
rc = - ENOMEM ;
goto out ;
}
2016-09-21 06:56:13 +03:00
2016-10-07 21:11:22 +03:00
build_ntlmssp_negotiate_blob ( ntlmssp_blob , ses ) ;
if ( use_spnego ) {
/* BB eventually need to add this */
cifs_dbg ( VFS , " spnego not supported for SMB2 yet \n " ) ;
rc = - EOPNOTSUPP ;
goto out ;
} else {
blob_length = sizeof ( struct _NEGOTIATE_MESSAGE ) ;
/* with raw NTLMSSP we don't encapsulate in SPNEGO */
}
sess_data - > iov [ 1 ] . iov_base = ntlmssp_blob ;
sess_data - > iov [ 1 ] . iov_len = blob_length ;
2016-09-21 06:56:13 +03:00
2016-10-07 21:11:22 +03:00
rc = SMB2_sess_sendreceive ( sess_data ) ;
rsp = ( struct smb2_sess_setup_rsp * ) sess_data - > iov [ 0 ] . iov_base ;
2011-12-27 16:22:00 +04:00
2016-10-07 21:11:22 +03:00
/* If true, rc here is expected and not an error */
if ( sess_data - > buf0_type ! = CIFS_NO_BUFFER & &
2018-06-01 03:53:06 +03:00
rsp - > sync_hdr . Status = = STATUS_MORE_PROCESSING_REQUIRED )
2016-10-07 21:11:22 +03:00
rc = 0 ;
2013-05-26 15:01:00 +04:00
2016-10-07 21:11:22 +03:00
if ( rc )
goto out ;
2011-12-27 16:22:00 +04:00
2018-06-01 03:53:07 +03:00
if ( offsetof ( struct smb2_sess_setup_rsp , Buffer ) ! =
2016-10-07 21:11:22 +03:00
le16_to_cpu ( rsp - > SecurityBufferOffset ) ) {
cifs_dbg ( VFS , " Invalid security buffer offset %d \n " ,
le16_to_cpu ( rsp - > SecurityBufferOffset ) ) ;
2011-12-27 16:22:00 +04:00
rc = - EIO ;
2016-10-07 21:11:22 +03:00
goto out ;
2011-12-27 16:22:00 +04:00
}
2016-10-07 21:11:22 +03:00
rc = decode_ntlmssp_challenge ( rsp - > Buffer ,
le16_to_cpu ( rsp - > SecurityBufferLength ) , ses ) ;
if ( rc )
goto out ;
2011-12-27 16:22:00 +04:00
2016-10-07 21:11:22 +03:00
cifs_dbg ( FYI , " rawntlmssp session setup challenge phase \n " ) ;
2011-12-27 16:22:00 +04:00
2018-06-01 03:53:06 +03:00
ses - > Suid = rsp - > sync_hdr . SessionId ;
2016-10-07 21:11:22 +03:00
ses - > session_flags = le16_to_cpu ( rsp - > SessionFlags ) ;
out :
kfree ( ntlmssp_blob ) ;
SMB2_sess_free_buffer ( sess_data ) ;
if ( ! rc ) {
sess_data - > result = 0 ;
sess_data - > func = SMB2_sess_auth_rawntlmssp_authenticate ;
return ;
}
out_err :
kfree ( ses - > ntlmssp ) ;
ses - > ntlmssp = NULL ;
sess_data - > result = rc ;
sess_data - > func = NULL ;
}
2011-12-27 16:22:00 +04:00
2016-10-07 21:11:22 +03:00
static void
SMB2_sess_auth_rawntlmssp_authenticate ( struct SMB2_sess_data * sess_data )
{
int rc ;
struct cifs_ses * ses = sess_data - > ses ;
struct smb2_sess_setup_req * req ;
struct smb2_sess_setup_rsp * rsp = NULL ;
unsigned char * ntlmssp_blob = NULL ;
bool use_spnego = false ; /* else use raw ntlmssp */
u16 blob_length = 0 ;
2011-12-27 16:22:00 +04:00
2016-10-07 21:11:22 +03:00
rc = SMB2_sess_alloc_buffer ( sess_data ) ;
if ( rc )
goto out ;
2011-12-27 16:22:00 +04:00
2016-10-07 21:11:22 +03:00
req = ( struct smb2_sess_setup_req * ) sess_data - > iov [ 0 ] . iov_base ;
2017-11-20 03:24:36 +03:00
req - > sync_hdr . SessionId = ses - > Suid ;
2016-10-07 21:11:22 +03:00
rc = build_ntlmssp_auth_blob ( & ntlmssp_blob , & blob_length , ses ,
sess_data - > nls_cp ) ;
if ( rc ) {
cifs_dbg ( FYI , " build_ntlmssp_auth_blob failed %d \n " , rc ) ;
goto out ;
2011-12-27 16:22:00 +04:00
}
2016-10-07 21:11:22 +03:00
if ( use_spnego ) {
/* BB eventually need to add this */
cifs_dbg ( VFS , " spnego not supported for SMB2 yet \n " ) ;
rc = - EOPNOTSUPP ;
goto out ;
}
sess_data - > iov [ 1 ] . iov_base = ntlmssp_blob ;
sess_data - > iov [ 1 ] . iov_len = blob_length ;
2011-12-27 16:22:00 +04:00
2016-10-07 21:11:22 +03:00
rc = SMB2_sess_sendreceive ( sess_data ) ;
if ( rc )
goto out ;
rsp = ( struct smb2_sess_setup_rsp * ) sess_data - > iov [ 0 ] . iov_base ;
2018-06-01 03:53:06 +03:00
ses - > Suid = rsp - > sync_hdr . SessionId ;
2011-12-27 16:22:00 +04:00
ses - > session_flags = le16_to_cpu ( rsp - > SessionFlags ) ;
2016-10-07 21:11:22 +03:00
rc = SMB2_sess_establish_session ( sess_data ) ;
out :
kfree ( ntlmssp_blob ) ;
SMB2_sess_free_buffer ( sess_data ) ;
kfree ( ses - > ntlmssp ) ;
ses - > ntlmssp = NULL ;
sess_data - > result = rc ;
sess_data - > func = NULL ;
}
2013-08-29 17:35:09 +04:00
2016-10-07 21:11:22 +03:00
static int
SMB2_select_sec ( struct cifs_ses * ses , struct SMB2_sess_data * sess_data )
{
2017-01-18 13:05:57 +03:00
int type ;
type = smb2_select_sectype ( ses - > server , ses - > sectype ) ;
cifs_dbg ( FYI , " sess setup type %d \n " , type ) ;
if ( type = = Unspecified ) {
cifs_dbg ( VFS ,
" Unable to select appropriate authentication method! " ) ;
return - EINVAL ;
}
2013-08-29 17:35:09 +04:00
2017-01-18 13:05:57 +03:00
switch ( type ) {
2016-10-07 21:11:22 +03:00
case Kerberos :
sess_data - > func = SMB2_auth_kerberos ;
break ;
case RawNTLMSSP :
sess_data - > func = SMB2_sess_auth_rawntlmssp_negotiate ;
break ;
default :
2017-01-18 13:05:57 +03:00
cifs_dbg ( VFS , " secType %d not supported! \n " , type ) ;
2016-10-07 21:11:22 +03:00
return - EOPNOTSUPP ;
2013-08-29 17:35:09 +04:00
}
2016-10-07 21:11:22 +03:00
return 0 ;
}
int
SMB2_sess_setup ( const unsigned int xid , struct cifs_ses * ses ,
const struct nls_table * nls_cp )
{
int rc = 0 ;
struct TCP_Server_Info * server = ses - > server ;
struct SMB2_sess_data * sess_data ;
cifs_dbg ( FYI , " Session Setup \n " ) ;
if ( ! server ) {
WARN ( 1 , " %s: server is NULL! \n " , __func__ ) ;
return - EIO ;
2013-08-29 17:35:09 +04:00
}
2016-10-07 21:11:22 +03:00
sess_data = kzalloc ( sizeof ( struct SMB2_sess_data ) , GFP_KERNEL ) ;
if ( ! sess_data )
return - ENOMEM ;
rc = SMB2_select_sec ( ses , sess_data ) ;
if ( rc )
goto out ;
sess_data - > xid = xid ;
sess_data - > ses = ses ;
sess_data - > buf0_type = CIFS_NO_BUFFER ;
sess_data - > nls_cp = ( struct nls_table * ) nls_cp ;
2018-05-31 23:19:25 +03:00
sess_data - > previous_session = ses - > Suid ;
2016-10-07 21:11:22 +03:00
2018-02-16 21:19:29 +03:00
/*
* Initialize the session hash with the server one .
*/
memcpy ( ses - > preauth_sha_hash , ses - > server - > preauth_sha_hash ,
SMB2_PREAUTH_HASH_SIZE ) ;
2016-10-07 21:11:22 +03:00
while ( sess_data - > func )
sess_data - > func ( sess_data ) ;
2017-09-20 02:40:03 +03:00
if ( ( ses - > session_flags & SMB2_SESSION_FLAG_IS_GUEST ) & & ( ses - > sign ) )
cifs_dbg ( VFS , " signing requested but authenticated as guest \n " ) ;
2016-10-07 21:11:21 +03:00
rc = sess_data - > result ;
2016-10-07 21:11:22 +03:00
out :
2016-10-07 21:11:21 +03:00
kfree ( sess_data ) ;
2011-12-27 16:22:00 +04:00
return rc ;
}
int
SMB2_logoff ( const unsigned int xid , struct cifs_ses * ses )
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2011-12-27 16:22:00 +04:00
struct smb2_logoff_req * req ; /* response is also trivial struct */
int rc = 0 ;
struct TCP_Server_Info * server ;
2016-10-31 23:49:30 +03:00
int flags = 0 ;
2017-11-09 04:14:17 +03:00
unsigned int total_len ;
struct kvec iov [ 1 ] ;
struct kvec rsp_iov ;
int resp_buf_type ;
2011-12-27 16:22:00 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " disconnect session %p \n " , ses ) ;
2011-12-27 16:22:00 +04:00
if ( ses & & ( ses - > server ) )
server = ses - > server ;
else
return - EIO ;
2013-10-03 14:44:45 +04:00
/* no need to send SMB logoff if uid already closed due to reconnect */
if ( ses - > need_reconnect )
goto smb2_session_already_dead ;
2017-11-09 04:14:17 +03:00
rc = smb2_plain_req_init ( SMB2_LOGOFF , NULL , ( void * * ) & req , & total_len ) ;
2011-12-27 16:22:00 +04:00
if ( rc )
return rc ;
/* since no tcon, smb2_init can not do this, so do here */
2017-11-09 04:14:17 +03:00
req - > sync_hdr . SessionId = ses - > Suid ;
2016-10-31 23:49:30 +03:00
if ( ses - > session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA )
flags | = CIFS_TRANSFORM_REQ ;
else if ( server - > sign )
2017-11-09 04:14:17 +03:00
req - > sync_hdr . Flags | = SMB2_FLAGS_SIGNED ;
flags | = CIFS_NO_RESP ;
iov [ 0 ] . iov_base = ( char * ) req ;
iov [ 0 ] . iov_len = total_len ;
2011-12-27 16:22:00 +04:00
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = 1 ;
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buf_type , flags , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( req ) ;
2011-12-27 16:22:00 +04:00
/*
* No tcon so can ' t do
* cifs_stats_inc ( & tcon - > stats . smb2_stats . smb2_com_fail [ SMB2 . . . ] ) ;
*/
2013-10-03 14:44:45 +04:00
smb2_session_already_dead :
2011-12-27 16:22:00 +04:00
return rc ;
}
2011-12-27 16:04:00 +04:00
static inline void cifs_stats_fail_inc ( struct cifs_tcon * tcon , uint16_t code )
{
2012-05-28 15:19:39 +04:00
cifs_stats_inc ( & tcon - > stats . smb2_stats . smb2_com_failed [ code ] ) ;
2011-12-27 16:04:00 +04:00
}
# define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */ )
2013-11-15 21:26:24 +04:00
/* These are similar values to what Windows uses */
static inline void init_copy_chunk_defaults ( struct cifs_tcon * tcon )
{
tcon - > max_chunks = 256 ;
tcon - > max_bytes_chunk = 1048576 ;
tcon - > max_bytes_copy = 16777216 ;
}
2011-12-27 16:04:00 +04:00
int
SMB2_tcon ( const unsigned int xid , struct cifs_ses * ses , const char * tree ,
struct cifs_tcon * tcon , const struct nls_table * cp )
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2011-12-27 16:04:00 +04:00
struct smb2_tree_connect_req * req ;
struct smb2_tree_connect_rsp * rsp = NULL ;
struct kvec iov [ 2 ] ;
2017-10-11 14:23:36 +03:00
struct kvec rsp_iov = { NULL , 0 } ;
2011-12-27 16:04:00 +04:00
int rc = 0 ;
int resp_buftype ;
int unc_path_len ;
__le16 * unc_path = NULL ;
2016-10-31 23:49:30 +03:00
int flags = 0 ;
2017-11-09 04:14:23 +03:00
unsigned int total_len ;
2011-12-27 16:04:00 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " TCON \n " ) ;
2011-12-27 16:04:00 +04:00
2017-07-09 13:45:04 +03:00
if ( ! ( ses - > server ) | | ! tree )
2011-12-27 16:04:00 +04:00
return - EIO ;
unc_path = kmalloc ( MAX_SHARENAME_LENGTH * 2 , GFP_KERNEL ) ;
if ( unc_path = = NULL )
return - ENOMEM ;
unc_path_len = cifs_strtoUTF16 ( unc_path , tree , strlen ( tree ) , cp ) + 1 ;
unc_path_len * = 2 ;
if ( unc_path_len < 2 ) {
kfree ( unc_path ) ;
return - EINVAL ;
}
2017-02-20 14:25:58 +03:00
/* SMB2 TREE_CONNECT request must be called with TreeId == 0 */
2018-01-24 15:46:10 +03:00
tcon - > tid = 0 ;
2018-10-20 01:14:32 +03:00
atomic_set ( & tcon - > num_remote_opens , 0 ) ;
2017-11-09 04:14:23 +03:00
rc = smb2_plain_req_init ( SMB2_TREE_CONNECT , tcon , ( void * * ) & req ,
& total_len ) ;
2011-12-27 16:04:00 +04:00
if ( rc ) {
kfree ( unc_path ) ;
return rc ;
}
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( tcon ) )
2016-11-18 00:59:23 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2011-12-27 16:04:00 +04:00
iov [ 0 ] . iov_base = ( char * ) req ;
2017-11-09 04:14:23 +03:00
/* 1 for pad */
iov [ 0 ] . iov_len = total_len - 1 ;
2011-12-27 16:04:00 +04:00
/* Testing shows that buffer offset must be at location of Buffer[0] */
req - > PathOffset = cpu_to_le16 ( sizeof ( struct smb2_tree_connect_req )
2017-11-09 04:14:23 +03:00
- 1 /* pad */ ) ;
2011-12-27 16:04:00 +04:00
req - > PathLength = cpu_to_le16 ( unc_path_len - 2 ) ;
iov [ 1 ] . iov_base = unc_path ;
iov [ 1 ] . iov_len = unc_path_len ;
2018-03-13 10:29:36 +03:00
/* 3.11 tcon req must be signed if not encrypted. See MS-SMB2 3.2.4.1.1 */
if ( ( ses - > server - > dialect = = SMB311_PROT_ID ) & &
2018-05-10 01:43:08 +03:00
! smb3_encryption_required ( tcon ) )
2018-03-13 10:29:36 +03:00
req - > sync_hdr . Flags | = SMB2_FLAGS_SIGNED ;
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = 2 ;
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( req ) ;
rsp = ( struct smb2_tree_connect_rsp * ) rsp_iov . iov_base ;
2018-10-28 08:47:11 +03:00
trace_smb3_tcon ( xid , tcon - > tid , ses - > Suid , tree , rc ) ;
2011-12-27 16:04:00 +04:00
if ( rc ! = 0 ) {
if ( tcon ) {
cifs_stats_fail_inc ( tcon , SMB2_TREE_CONNECT_HE ) ;
tcon - > need_reconnect = true ;
}
goto tcon_error_exit ;
}
2017-05-12 18:59:32 +03:00
switch ( rsp - > ShareType ) {
case SMB2_SHARE_TYPE_DISK :
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " connection to disk share \n " ) ;
2017-05-12 18:59:32 +03:00
break ;
case SMB2_SHARE_TYPE_PIPE :
2018-01-24 15:46:10 +03:00
tcon - > pipe = true ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " connection to pipe share \n " ) ;
2017-05-12 18:59:32 +03:00
break ;
case SMB2_SHARE_TYPE_PRINT :
2018-01-24 15:46:10 +03:00
tcon - > print = true ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " connection to printer \n " ) ;
2017-05-12 18:59:32 +03:00
break ;
default :
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " unknown share type %d \n " , rsp - > ShareType ) ;
2011-12-27 16:04:00 +04:00
rc = - EOPNOTSUPP ;
goto tcon_error_exit ;
}
tcon - > share_flags = le32_to_cpu ( rsp - > ShareFlags ) ;
2013-06-19 23:15:30 +04:00
tcon - > capabilities = rsp - > Capabilities ; /* we keep caps little endian */
2011-12-27 16:04:00 +04:00
tcon - > maximal_access = le32_to_cpu ( rsp - > MaximalAccess ) ;
tcon - > tidStatus = CifsGood ;
tcon - > need_reconnect = false ;
2018-06-01 03:53:06 +03:00
tcon - > tid = rsp - > sync_hdr . TreeId ;
2013-06-24 10:57:47 +04:00
strlcpy ( tcon - > treeName , tree , sizeof ( tcon - > treeName ) ) ;
2011-12-27 16:04:00 +04:00
if ( ( rsp - > Capabilities & SMB2_SHARE_CAP_DFS ) & &
( ( tcon - > share_flags & SHI1005_FLAGS_DFS ) = = 0 ) )
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " DFS capability contradicts DFS flag \n " ) ;
2016-11-18 00:59:23 +03:00
if ( tcon - > seal & &
! ( tcon - > ses - > server - > capabilities & SMB2_GLOBAL_CAP_ENCRYPTION ) )
cifs_dbg ( VFS , " Encryption is requested but not supported \n " ) ;
2013-11-15 21:26:24 +04:00
init_copy_chunk_defaults ( tcon ) ;
2013-11-20 09:44:46 +04:00
if ( tcon - > ses - > server - > ops - > validate_negotiate )
rc = tcon - > ses - > server - > ops - > validate_negotiate ( xid , tcon ) ;
2011-12-27 16:04:00 +04:00
tcon_exit :
2018-10-28 08:47:11 +03:00
2011-12-27 16:04:00 +04:00
free_rsp_buf ( resp_buftype , rsp ) ;
kfree ( unc_path ) ;
return rc ;
tcon_error_exit :
2018-06-01 03:53:06 +03:00
if ( rsp & & rsp - > sync_hdr . Status = = STATUS_BAD_NETWORK_NAME ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " BAD_NETWORK_NAME: %s \n " , tree ) ;
2011-12-27 16:04:00 +04:00
}
goto tcon_exit ;
}
int
SMB2_tdis ( const unsigned int xid , struct cifs_tcon * tcon )
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2011-12-27 16:04:00 +04:00
struct smb2_tree_disconnect_req * req ; /* response is trivial */
int rc = 0 ;
struct cifs_ses * ses = tcon - > ses ;
2016-10-31 23:49:30 +03:00
int flags = 0 ;
2017-11-09 04:14:18 +03:00
unsigned int total_len ;
struct kvec iov [ 1 ] ;
struct kvec rsp_iov ;
int resp_buf_type ;
2011-12-27 16:04:00 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Tree Disconnect \n " ) ;
2011-12-27 16:04:00 +04:00
2017-07-09 13:45:04 +03:00
if ( ! ses | | ! ( ses - > server ) )
2011-12-27 16:04:00 +04:00
return - EIO ;
if ( ( tcon - > need_reconnect ) | | ( tcon - > ses - > need_reconnect ) )
return 0 ;
2017-11-09 04:14:18 +03:00
rc = smb2_plain_req_init ( SMB2_TREE_DISCONNECT , tcon , ( void * * ) & req ,
& total_len ) ;
2011-12-27 16:04:00 +04:00
if ( rc )
return rc ;
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2017-11-09 04:14:18 +03:00
flags | = CIFS_NO_RESP ;
iov [ 0 ] . iov_base = ( char * ) req ;
iov [ 0 ] . iov_len = total_len ;
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = 1 ;
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buf_type , flags , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( req ) ;
2011-12-27 16:04:00 +04:00
if ( rc )
cifs_stats_fail_inc ( tcon , SMB2_TREE_DISCONNECT_HE ) ;
return rc ;
}
2011-12-26 22:58:46 +04:00
2012-09-19 17:22:44 +04:00
2013-07-04 18:41:09 +04:00
static struct create_durable *
create_durable_buf ( void )
{
struct create_durable * buf ;
buf = kzalloc ( sizeof ( struct create_durable ) , GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
2013-07-09 18:40:58 +04:00
( struct create_durable , Data ) ) ;
2013-07-04 18:41:09 +04:00
buf - > ccontext . DataLength = cpu_to_le32 ( 16 ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct create_durable , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
2014-05-14 16:29:40 +04:00
/* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */
2013-07-04 18:41:09 +04:00
buf - > Name [ 0 ] = ' D ' ;
buf - > Name [ 1 ] = ' H ' ;
buf - > Name [ 2 ] = ' n ' ;
buf - > Name [ 3 ] = ' Q ' ;
return buf ;
}
2013-07-09 18:40:58 +04:00
static struct create_durable *
create_reconnect_durable_buf ( struct cifs_fid * fid )
{
struct create_durable * buf ;
buf = kzalloc ( sizeof ( struct create_durable ) , GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
( struct create_durable , Data ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( 16 ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct create_durable , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
buf - > Data . Fid . PersistentFileId = fid - > persistent_fid ;
buf - > Data . Fid . VolatileFileId = fid - > volatile_fid ;
2014-05-14 16:29:40 +04:00
/* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */
2013-07-09 18:40:58 +04:00
buf - > Name [ 0 ] = ' D ' ;
buf - > Name [ 1 ] = ' H ' ;
buf - > Name [ 2 ] = ' n ' ;
buf - > Name [ 3 ] = ' C ' ;
return buf ;
}
2012-09-19 17:22:44 +04:00
static __u8
2013-09-05 21:30:16 +04:00
parse_lease_state ( struct TCP_Server_Info * server , struct smb2_create_rsp * rsp ,
2018-04-26 17:10:18 +03:00
unsigned int * epoch , char * lease_key )
2012-09-19 17:22:44 +04:00
{
char * data_offset ;
2013-09-05 20:16:45 +04:00
struct create_context * cc ;
2016-02-10 02:52:08 +03:00
unsigned int next ;
unsigned int remaining ;
2013-07-09 19:44:56 +04:00
char * name ;
2012-09-19 17:22:44 +04:00
2018-06-01 03:53:07 +03:00
data_offset = ( char * ) rsp + le32_to_cpu ( rsp - > CreateContextsOffset ) ;
2016-02-10 02:52:08 +03:00
remaining = le32_to_cpu ( rsp - > CreateContextsLength ) ;
2013-09-05 20:16:45 +04:00
cc = ( struct create_context * ) data_offset ;
2016-02-10 02:52:08 +03:00
while ( remaining > = sizeof ( struct create_context ) ) {
2013-09-05 20:16:45 +04:00
name = le16_to_cpu ( cc - > NameOffset ) + ( char * ) cc ;
2016-02-10 02:52:08 +03:00
if ( le16_to_cpu ( cc - > NameLength ) = = 4 & &
strncmp ( name , " RqLs " , 4 ) = = 0 )
2018-04-26 17:10:18 +03:00
return server - > ops - > parse_lease_buf ( cc , epoch ,
lease_key ) ;
2016-02-10 02:52:08 +03:00
next = le32_to_cpu ( cc - > Next ) ;
if ( ! next )
break ;
remaining - = next ;
cc = ( struct create_context * ) ( ( char * ) cc + next ) ;
}
2012-09-19 17:22:44 +04:00
2013-09-05 20:16:45 +04:00
return 0 ;
2012-09-19 17:22:44 +04:00
}
2013-07-04 19:10:00 +04:00
static int
2013-09-04 13:07:41 +04:00
add_lease_context ( struct TCP_Server_Info * server , struct kvec * iov ,
2018-07-05 16:10:02 +03:00
unsigned int * num_iovec , u8 * lease_key , __u8 * oplock )
2013-07-04 19:10:00 +04:00
{
struct smb2_create_req * req = iov [ 0 ] . iov_base ;
unsigned int num = * num_iovec ;
2018-07-05 16:10:02 +03:00
iov [ num ] . iov_base = server - > ops - > create_lease_buf ( lease_key , * oplock ) ;
2013-07-04 19:10:00 +04:00
if ( iov [ num ] . iov_base = = NULL )
return - ENOMEM ;
2013-09-04 13:07:41 +04:00
iov [ num ] . iov_len = server - > vals - > create_lease_size ;
2013-07-04 19:10:00 +04:00
req - > RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE ;
if ( ! req - > CreateContextsOffset )
req - > CreateContextsOffset = cpu_to_le32 (
2017-11-20 03:24:38 +03:00
sizeof ( struct smb2_create_req ) +
2013-07-04 19:10:00 +04:00
iov [ num - 1 ] . iov_len ) ;
2013-09-04 13:07:41 +04:00
le32_add_cpu ( & req - > CreateContextsLength ,
server - > vals - > create_lease_size ) ;
2013-07-04 19:10:00 +04:00
* num_iovec = num + 1 ;
return 0 ;
}
2015-11-03 18:26:27 +03:00
static struct create_durable_v2 *
create_durable_v2_buf ( struct cifs_fid * pfid )
{
struct create_durable_v2 * buf ;
buf = kzalloc ( sizeof ( struct create_durable_v2 ) , GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
( struct create_durable_v2 , dcontext ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( sizeof ( struct durable_context_v2 ) ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct create_durable_v2 , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
buf - > dcontext . Timeout = 0 ; /* Should this be configurable by workload */
buf - > dcontext . Flags = cpu_to_le32 ( SMB2_DHANDLE_FLAG_PERSISTENT ) ;
2016-09-22 08:39:34 +03:00
generate_random_uuid ( buf - > dcontext . CreateGuid ) ;
2015-11-03 18:26:27 +03:00
memcpy ( pfid - > create_guid , buf - > dcontext . CreateGuid , 16 ) ;
/* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */
buf - > Name [ 0 ] = ' D ' ;
buf - > Name [ 1 ] = ' H ' ;
buf - > Name [ 2 ] = ' 2 ' ;
buf - > Name [ 3 ] = ' Q ' ;
return buf ;
}
static struct create_durable_handle_reconnect_v2 *
create_reconnect_durable_v2_buf ( struct cifs_fid * fid )
{
struct create_durable_handle_reconnect_v2 * buf ;
buf = kzalloc ( sizeof ( struct create_durable_handle_reconnect_v2 ) ,
GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
buf - > ccontext . DataOffset =
cpu_to_le16 ( offsetof ( struct create_durable_handle_reconnect_v2 ,
dcontext ) ) ;
buf - > ccontext . DataLength =
cpu_to_le32 ( sizeof ( struct durable_reconnect_context_v2 ) ) ;
buf - > ccontext . NameOffset =
cpu_to_le16 ( offsetof ( struct create_durable_handle_reconnect_v2 ,
Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
buf - > dcontext . Fid . PersistentFileId = fid - > persistent_fid ;
buf - > dcontext . Fid . VolatileFileId = fid - > volatile_fid ;
buf - > dcontext . Flags = cpu_to_le32 ( SMB2_DHANDLE_FLAG_PERSISTENT ) ;
memcpy ( buf - > dcontext . CreateGuid , fid - > create_guid , 16 ) ;
/* SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 is "DH2C" */
buf - > Name [ 0 ] = ' D ' ;
buf - > Name [ 1 ] = ' H ' ;
buf - > Name [ 2 ] = ' 2 ' ;
buf - > Name [ 3 ] = ' C ' ;
return buf ;
}
2013-07-04 18:41:09 +04:00
static int
2015-11-03 18:26:27 +03:00
add_durable_v2_context ( struct kvec * iov , unsigned int * num_iovec ,
struct cifs_open_parms * oparms )
{
struct smb2_create_req * req = iov [ 0 ] . iov_base ;
unsigned int num = * num_iovec ;
iov [ num ] . iov_base = create_durable_v2_buf ( oparms - > fid ) ;
if ( iov [ num ] . iov_base = = NULL )
return - ENOMEM ;
iov [ num ] . iov_len = sizeof ( struct create_durable_v2 ) ;
if ( ! req - > CreateContextsOffset )
req - > CreateContextsOffset =
2017-11-20 03:24:38 +03:00
cpu_to_le32 ( sizeof ( struct smb2_create_req ) +
2015-11-03 18:26:27 +03:00
iov [ 1 ] . iov_len ) ;
le32_add_cpu ( & req - > CreateContextsLength , sizeof ( struct create_durable_v2 ) ) ;
* num_iovec = num + 1 ;
return 0 ;
}
static int
add_durable_reconnect_v2_context ( struct kvec * iov , unsigned int * num_iovec ,
2013-07-09 18:40:58 +04:00
struct cifs_open_parms * oparms )
2013-07-04 18:41:09 +04:00
{
struct smb2_create_req * req = iov [ 0 ] . iov_base ;
unsigned int num = * num_iovec ;
2015-11-03 18:26:27 +03:00
/* indicate that we don't need to relock the file */
oparms - > reconnect = false ;
iov [ num ] . iov_base = create_reconnect_durable_v2_buf ( oparms - > fid ) ;
if ( iov [ num ] . iov_base = = NULL )
return - ENOMEM ;
iov [ num ] . iov_len = sizeof ( struct create_durable_handle_reconnect_v2 ) ;
if ( ! req - > CreateContextsOffset )
req - > CreateContextsOffset =
2017-11-20 03:24:38 +03:00
cpu_to_le32 ( sizeof ( struct smb2_create_req ) +
2015-11-03 18:26:27 +03:00
iov [ 1 ] . iov_len ) ;
le32_add_cpu ( & req - > CreateContextsLength ,
sizeof ( struct create_durable_handle_reconnect_v2 ) ) ;
* num_iovec = num + 1 ;
return 0 ;
}
static int
add_durable_context ( struct kvec * iov , unsigned int * num_iovec ,
struct cifs_open_parms * oparms , bool use_persistent )
{
struct smb2_create_req * req = iov [ 0 ] . iov_base ;
unsigned int num = * num_iovec ;
if ( use_persistent ) {
if ( oparms - > reconnect )
return add_durable_reconnect_v2_context ( iov , num_iovec ,
oparms ) ;
else
return add_durable_v2_context ( iov , num_iovec , oparms ) ;
}
2013-07-09 18:40:58 +04:00
if ( oparms - > reconnect ) {
iov [ num ] . iov_base = create_reconnect_durable_buf ( oparms - > fid ) ;
/* indicate that we don't need to relock the file */
oparms - > reconnect = false ;
} else
iov [ num ] . iov_base = create_durable_buf ( ) ;
2013-07-04 18:41:09 +04:00
if ( iov [ num ] . iov_base = = NULL )
return - ENOMEM ;
iov [ num ] . iov_len = sizeof ( struct create_durable ) ;
if ( ! req - > CreateContextsOffset )
req - > CreateContextsOffset =
2017-11-20 03:24:38 +03:00
cpu_to_le32 ( sizeof ( struct smb2_create_req ) +
2013-07-04 18:41:09 +04:00
iov [ 1 ] . iov_len ) ;
2013-08-26 10:34:46 +04:00
le32_add_cpu ( & req - > CreateContextsLength , sizeof ( struct create_durable ) ) ;
2013-07-04 18:41:09 +04:00
* num_iovec = num + 1 ;
return 0 ;
}
smb3: allow previous versions to be mounted with snapshot= mount parm
mounting with the "snapshots=" mount parm allows a read-only
view of a previous version of a file system (see MS-SMB2
and "timewarp" tokens, section 2.2.13.2.6) based on the timestamp
passed in on the snapshots mount parm.
Add processing to optionally send this create context.
Example output:
/mnt1 is mounted with "snapshots=..." and will see an earlier
version of the directory, with three fewer files than /mnt2
the current version of the directory.
root@Ubuntu-17-Virtual-Machine:~/cifs-2.6# cat /proc/mounts | grep cifs
//172.22.149.186/public /mnt1 cifs
ro,relatime,vers=default,cache=strict,username=smfrench,uid=0,noforceuid,gid=0,noforcegid,addr=172.22.149.186,file_mode=0755,dir_mode=0755,soft,nounix,mapposix,rsize=1048576,wsize=1048576,echo_interval=60,snapshot=131748608570000000,actimeo=1
//172.22.149.186/public /mnt2 cifs
rw,relatime,vers=default,cache=strict,username=smfrench,uid=0,noforceuid,gid=0,noforcegid,addr=172.22.149.186,file_mode=0755,dir_mode=0755,soft,nounix,mapposix,rsize=1048576,wsize=1048576,echo_interval=60,actimeo=1
root@Ubuntu-17-Virtual-Machine:~/cifs-2.6# ls /mnt1
EmptyDir newerdir
root@Ubuntu-17-Virtual-Machine:~/cifs-2.6# ls /mnt1/newerdir
root@Ubuntu-17-Virtual-Machine:~/cifs-2.6# ls /mnt2
EmptyDir file newerdir newestdir timestamp-trace.cap
root@Ubuntu-17-Virtual-Machine:~/cifs-2.6# ls /mnt2/newerdir
new-file-not-in-snapshot
Snapshots are extremely useful for comparing previous versions of files or directories,
and recovering from data corruptions or mistakes.
Signed-off-by: Steve French <stfrench@microsoft.com>
Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com>
2018-08-10 10:25:06 +03:00
/* See MS-SMB2 2.2.13.2.7 */
static struct crt_twarp_ctxt *
create_twarp_buf ( __u64 timewarp )
{
struct crt_twarp_ctxt * buf ;
buf = kzalloc ( sizeof ( struct crt_twarp_ctxt ) , GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
buf - > ccontext . DataOffset = cpu_to_le16 ( offsetof
( struct crt_twarp_ctxt , Timestamp ) ) ;
buf - > ccontext . DataLength = cpu_to_le32 ( 8 ) ;
buf - > ccontext . NameOffset = cpu_to_le16 ( offsetof
( struct crt_twarp_ctxt , Name ) ) ;
buf - > ccontext . NameLength = cpu_to_le16 ( 4 ) ;
/* SMB2_CREATE_TIMEWARP_TOKEN is "TWrp" */
buf - > Name [ 0 ] = ' T ' ;
buf - > Name [ 1 ] = ' W ' ;
buf - > Name [ 2 ] = ' r ' ;
buf - > Name [ 3 ] = ' p ' ;
buf - > Timestamp = cpu_to_le64 ( timewarp ) ;
return buf ;
}
/* See MS-SMB2 2.2.13.2.7 */
static int
add_twarp_context ( struct kvec * iov , unsigned int * num_iovec , __u64 timewarp )
{
struct smb2_create_req * req = iov [ 0 ] . iov_base ;
unsigned int num = * num_iovec ;
iov [ num ] . iov_base = create_twarp_buf ( timewarp ) ;
if ( iov [ num ] . iov_base = = NULL )
return - ENOMEM ;
iov [ num ] . iov_len = sizeof ( struct crt_twarp_ctxt ) ;
if ( ! req - > CreateContextsOffset )
req - > CreateContextsOffset = cpu_to_le32 (
sizeof ( struct smb2_create_req ) +
iov [ num - 1 ] . iov_len ) ;
le32_add_cpu ( & req - > CreateContextsLength , sizeof ( struct crt_twarp_ctxt ) ) ;
* num_iovec = num + 1 ;
return 0 ;
}
2017-02-22 16:47:17 +03:00
static int
alloc_path_with_tree_prefix ( __le16 * * out_path , int * out_size , int * out_len ,
const char * treename , const __le16 * path )
{
int treename_len , path_len ;
struct nls_table * cp ;
const __le16 sep [ ] = { cpu_to_le16 ( ' \\ ' ) , cpu_to_le16 ( 0x0000 ) } ;
/*
* skip leading " \\ "
*/
treename_len = strlen ( treename ) ;
if ( treename_len < 2 | | ! ( treename [ 0 ] = = ' \\ ' & & treename [ 1 ] = = ' \\ ' ) )
return - EINVAL ;
treename + = 2 ;
treename_len - = 2 ;
path_len = UniStrnlen ( ( wchar_t * ) path , PATH_MAX ) ;
/*
* make room for one path separator between the treename and
* path
*/
* out_len = treename_len + 1 + path_len ;
/*
* final path needs to be null - terminated UTF16 with a
* size aligned to 8
*/
* out_size = roundup ( ( * out_len + 1 ) * 2 , 8 ) ;
* out_path = kzalloc ( * out_size , GFP_KERNEL ) ;
if ( ! * out_path )
return - ENOMEM ;
cp = load_nls_default ( ) ;
cifs_strtoUTF16 ( * out_path , treename , treename_len , cp ) ;
UniStrcat ( * out_path , sep ) ;
UniStrcat ( * out_path , path ) ;
unload_nls ( cp ) ;
return 0 ;
}
2018-06-15 05:56:32 +03:00
int smb311_posix_mkdir ( const unsigned int xid , struct inode * inode ,
umode_t mode , struct cifs_tcon * tcon ,
const char * full_path ,
struct cifs_sb_info * cifs_sb )
{
struct smb_rqst rqst ;
struct smb2_create_req * req ;
2018-06-20 01:18:48 +03:00
struct smb2_create_rsp * rsp = NULL ;
2018-06-15 05:56:32 +03:00
struct cifs_ses * ses = tcon - > ses ;
struct kvec iov [ 3 ] ; /* make sure at least one for each open context */
struct kvec rsp_iov = { NULL , 0 } ;
int resp_buftype ;
int uni_path_len ;
__le16 * copy_path = NULL ;
int copy_size ;
int rc = 0 ;
unsigned int n_iov = 2 ;
__u32 file_attributes = 0 ;
char * pc_buf = NULL ;
int flags = 0 ;
unsigned int total_len ;
2018-06-20 01:18:48 +03:00
__le16 * utf16_path = NULL ;
2018-06-15 05:56:32 +03:00
cifs_dbg ( FYI , " mkdir \n " ) ;
2018-06-20 01:18:48 +03:00
/* resource #1: path allocation */
utf16_path = cifs_convert_path_to_utf16 ( full_path , cifs_sb ) ;
if ( ! utf16_path )
return - ENOMEM ;
2018-12-18 05:51:51 +03:00
if ( ! ses | | ! ( ses - > server ) ) {
2018-06-20 01:18:48 +03:00
rc = - EIO ;
goto err_free_path ;
}
2018-06-15 05:56:32 +03:00
2018-06-20 01:18:48 +03:00
/* resource #2: request */
2018-06-15 05:56:32 +03:00
rc = smb2_plain_req_init ( SMB2_CREATE , tcon , ( void * * ) & req , & total_len ) ;
if ( rc )
2018-06-20 01:18:48 +03:00
goto err_free_path ;
2018-06-15 05:56:32 +03:00
if ( smb3_encryption_required ( tcon ) )
flags | = CIFS_TRANSFORM_REQ ;
req - > ImpersonationLevel = IL_IMPERSONATION ;
req - > DesiredAccess = cpu_to_le32 ( FILE_WRITE_ATTRIBUTES ) ;
/* File attributes ignored on open (used in create though) */
req - > FileAttributes = cpu_to_le32 ( file_attributes ) ;
req - > ShareAccess = FILE_SHARE_ALL_LE ;
req - > CreateDisposition = cpu_to_le32 ( FILE_CREATE ) ;
req - > CreateOptions = cpu_to_le32 ( CREATE_NOT_FILE ) ;
iov [ 0 ] . iov_base = ( char * ) req ;
/* -1 since last byte is buf[0] which is sent below (path) */
iov [ 0 ] . iov_len = total_len - 1 ;
req - > NameOffset = cpu_to_le16 ( sizeof ( struct smb2_create_req ) ) ;
/* [MS-SMB2] 2.2.13 NameOffset:
* If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of
* the SMB2 header , the file name includes a prefix that will
* be processed during DFS name normalization as specified in
* section 3.3 .5 .9 . Otherwise , the file name is relative to
* the share that is identified by the TreeId in the SMB2
* header .
*/
if ( tcon - > share_flags & SHI1005_FLAGS_DFS ) {
int name_len ;
req - > sync_hdr . Flags | = SMB2_FLAGS_DFS_OPERATIONS ;
rc = alloc_path_with_tree_prefix ( & copy_path , & copy_size ,
& name_len ,
2018-06-20 01:18:48 +03:00
tcon - > treeName , utf16_path ) ;
if ( rc )
goto err_free_req ;
2018-06-15 05:56:32 +03:00
req - > NameLength = cpu_to_le16 ( name_len * 2 ) ;
uni_path_len = copy_size ;
2018-06-20 01:18:48 +03:00
/* free before overwriting resource */
kfree ( utf16_path ) ;
utf16_path = copy_path ;
2018-06-15 05:56:32 +03:00
} else {
2018-06-20 01:18:48 +03:00
uni_path_len = ( 2 * UniStrnlen ( ( wchar_t * ) utf16_path , PATH_MAX ) ) + 2 ;
2018-06-15 05:56:32 +03:00
/* MUST set path len (NameLength) to 0 opening root of share */
req - > NameLength = cpu_to_le16 ( uni_path_len - 2 ) ;
if ( uni_path_len % 8 ! = 0 ) {
copy_size = roundup ( uni_path_len , 8 ) ;
copy_path = kzalloc ( copy_size , GFP_KERNEL ) ;
if ( ! copy_path ) {
2018-06-20 01:18:48 +03:00
rc = - ENOMEM ;
goto err_free_req ;
2018-06-15 05:56:32 +03:00
}
2018-06-20 01:18:48 +03:00
memcpy ( ( char * ) copy_path , ( const char * ) utf16_path ,
2018-06-15 05:56:32 +03:00
uni_path_len ) ;
uni_path_len = copy_size ;
2018-06-20 01:18:48 +03:00
/* free before overwriting resource */
kfree ( utf16_path ) ;
utf16_path = copy_path ;
2018-06-15 05:56:32 +03:00
}
}
iov [ 1 ] . iov_len = uni_path_len ;
2018-06-20 01:18:48 +03:00
iov [ 1 ] . iov_base = utf16_path ;
2018-06-15 05:56:32 +03:00
req - > RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE ;
if ( tcon - > posix_extensions ) {
2018-06-20 01:18:48 +03:00
/* resource #3: posix buf */
2018-06-15 05:56:32 +03:00
rc = add_posix_context ( iov , & n_iov , mode ) ;
2018-06-20 01:18:48 +03:00
if ( rc )
goto err_free_req ;
2018-06-15 05:56:32 +03:00
pc_buf = iov [ n_iov - 1 ] . iov_base ;
}
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = n_iov ;
2018-06-20 01:18:48 +03:00
/* resource #4: response buffer */
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags , & rsp_iov ) ;
if ( rc ) {
2018-06-15 05:56:32 +03:00
cifs_stats_fail_inc ( tcon , SMB2_CREATE_HE ) ;
trace_smb3_posix_mkdir_err ( xid , tcon - > tid , ses - > Suid ,
2018-06-20 01:18:48 +03:00
CREATE_NOT_FILE ,
FILE_WRITE_ATTRIBUTES , rc ) ;
goto err_free_rsp_buf ;
}
rsp = ( struct smb2_create_rsp * ) rsp_iov . iov_base ;
trace_smb3_posix_mkdir_done ( xid , rsp - > PersistentFileId , tcon - > tid ,
ses - > Suid , CREATE_NOT_FILE ,
FILE_WRITE_ATTRIBUTES ) ;
2018-06-15 05:56:32 +03:00
SMB2_close ( xid , tcon , rsp - > PersistentFileId , rsp - > VolatileFileId ) ;
/* Eventually save off posix specific response info and timestaps */
2018-06-20 01:18:48 +03:00
err_free_rsp_buf :
2018-06-15 05:56:32 +03:00
free_rsp_buf ( resp_buftype , rsp ) ;
2018-06-20 01:18:48 +03:00
kfree ( pc_buf ) ;
err_free_req :
cifs_small_buf_release ( req ) ;
err_free_path :
kfree ( utf16_path ) ;
2018-06-15 05:56:32 +03:00
return rc ;
}
2011-12-26 22:58:46 +04:00
int
2018-08-08 08:07:46 +03:00
SMB2_open_init ( struct cifs_tcon * tcon , struct smb_rqst * rqst , __u8 * oplock ,
struct cifs_open_parms * oparms , __le16 * path )
2011-12-26 22:58:46 +04:00
{
2018-08-08 08:07:46 +03:00
struct TCP_Server_Info * server = tcon - > ses - > server ;
2011-12-26 22:58:46 +04:00
struct smb2_create_req * req ;
2016-10-25 21:38:47 +03:00
unsigned int n_iov = 2 ;
2013-07-05 12:21:26 +04:00
__u32 file_attributes = 0 ;
2018-08-08 08:07:46 +03:00
int copy_size ;
int uni_path_len ;
2017-11-20 03:24:38 +03:00
unsigned int total_len ;
2018-08-08 08:07:46 +03:00
struct kvec * iov = rqst - > rq_iov ;
__le16 * copy_path ;
int rc ;
2011-12-26 22:58:46 +04:00
2017-11-20 03:24:38 +03:00
rc = smb2_plain_req_init ( SMB2_CREATE , tcon , ( void * * ) & req , & total_len ) ;
2011-12-26 22:58:46 +04:00
if ( rc )
return rc ;
2018-08-08 08:07:46 +03:00
iov [ 0 ] . iov_base = ( char * ) req ;
/* -1 since last byte is buf[0] which is sent below (path) */
iov [ 0 ] . iov_len = total_len - 1 ;
2016-10-31 23:49:30 +03:00
2013-07-09 18:20:30 +04:00
if ( oparms - > create_options & CREATE_OPTION_READONLY )
2013-07-05 12:21:26 +04:00
file_attributes | = ATTR_READONLY ;
2014-09-22 14:13:55 +04:00
if ( oparms - > create_options & CREATE_OPTION_SPECIAL )
file_attributes | = ATTR_SYSTEM ;
2013-07-05 12:21:26 +04:00
2011-12-26 22:58:46 +04:00
req - > ImpersonationLevel = IL_IMPERSONATION ;
2013-07-09 18:20:30 +04:00
req - > DesiredAccess = cpu_to_le32 ( oparms - > desired_access ) ;
2011-12-26 22:58:46 +04:00
/* File attributes ignored on open (used in create though) */
req - > FileAttributes = cpu_to_le32 ( file_attributes ) ;
req - > ShareAccess = FILE_SHARE_ALL_LE ;
2013-07-09 18:20:30 +04:00
req - > CreateDisposition = cpu_to_le32 ( oparms - > disposition ) ;
req - > CreateOptions = cpu_to_le32 ( oparms - > create_options & CREATE_OPTIONS_MASK ) ;
2017-11-20 03:24:38 +03:00
req - > NameOffset = cpu_to_le16 ( sizeof ( struct smb2_create_req ) ) ;
2017-02-22 16:47:17 +03:00
/* [MS-SMB2] 2.2.13 NameOffset:
* If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of
* the SMB2 header , the file name includes a prefix that will
* be processed during DFS name normalization as specified in
* section 3.3 .5 .9 . Otherwise , the file name is relative to
* the share that is identified by the TreeId in the SMB2
* header .
*/
if ( tcon - > share_flags & SHI1005_FLAGS_DFS ) {
int name_len ;
2017-11-20 03:24:38 +03:00
req - > sync_hdr . Flags | = SMB2_FLAGS_DFS_OPERATIONS ;
2017-02-22 16:47:17 +03:00
rc = alloc_path_with_tree_prefix ( & copy_path , & copy_size ,
& name_len ,
tcon - > treeName , path ) ;
2018-08-08 08:07:46 +03:00
if ( rc )
2017-02-22 16:47:17 +03:00
return rc ;
req - > NameLength = cpu_to_le16 ( name_len * 2 ) ;
2013-07-04 19:41:24 +04:00
uni_path_len = copy_size ;
path = copy_path ;
2017-02-22 16:47:17 +03:00
} else {
uni_path_len = ( 2 * UniStrnlen ( ( wchar_t * ) path , PATH_MAX ) ) + 2 ;
/* MUST set path len (NameLength) to 0 opening root of share */
req - > NameLength = cpu_to_le16 ( uni_path_len - 2 ) ;
2018-08-08 08:07:46 +03:00
copy_size = uni_path_len ;
if ( copy_size % 8 ! = 0 )
copy_size = roundup ( copy_size , 8 ) ;
copy_path = kzalloc ( copy_size , GFP_KERNEL ) ;
if ( ! copy_path )
return - ENOMEM ;
memcpy ( ( char * ) copy_path , ( const char * ) path ,
uni_path_len ) ;
uni_path_len = copy_size ;
path = copy_path ;
2011-12-26 22:58:46 +04:00
}
2013-07-04 19:41:24 +04:00
iov [ 1 ] . iov_len = uni_path_len ;
iov [ 1 ] . iov_base = path ;
2012-09-19 17:22:44 +04:00
if ( ! server - > oplocks )
* oplock = SMB2_OPLOCK_LEVEL_NONE ;
2013-09-04 13:07:41 +04:00
if ( ! ( server - > capabilities & SMB2_GLOBAL_CAP_LEASING ) | |
2012-09-19 17:22:44 +04:00
* oplock = = SMB2_OPLOCK_LEVEL_NONE )
req - > RequestedOplockLevel = * oplock ;
2018-08-31 23:12:10 +03:00
else if ( ! ( server - > capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING ) & &
( oparms - > create_options & CREATE_NOT_FILE ) )
req - > RequestedOplockLevel = * oplock ; /* no srv lease support */
2012-09-19 17:22:44 +04:00
else {
2018-07-05 16:10:02 +03:00
rc = add_lease_context ( server , iov , & n_iov ,
oparms - > fid - > lease_key , oplock ) ;
2018-08-08 08:07:46 +03:00
if ( rc )
2013-07-04 19:10:00 +04:00
return rc ;
2012-09-19 17:22:44 +04:00
}
2013-07-04 18:41:09 +04:00
if ( * oplock = = SMB2_OPLOCK_LEVEL_BATCH ) {
/* need to set Next field of lease context if we request it */
2013-09-04 13:07:41 +04:00
if ( server - > capabilities & SMB2_GLOBAL_CAP_LEASING ) {
2013-07-04 18:41:09 +04:00
struct create_context * ccontext =
2016-10-25 21:38:47 +03:00
( struct create_context * ) iov [ n_iov - 1 ] . iov_base ;
2013-07-10 21:50:57 +04:00
ccontext - > Next =
2013-09-04 13:07:41 +04:00
cpu_to_le32 ( server - > vals - > create_lease_size ) ;
2013-07-04 18:41:09 +04:00
}
2015-11-03 18:26:27 +03:00
2016-10-25 21:38:47 +03:00
rc = add_durable_context ( iov , & n_iov , oparms ,
2015-11-03 18:26:27 +03:00
tcon - > use_persistent ) ;
2018-08-08 08:07:46 +03:00
if ( rc )
2013-07-04 18:41:09 +04:00
return rc ;
}
2018-06-01 03:16:54 +03:00
if ( tcon - > posix_extensions ) {
if ( n_iov > 2 ) {
struct create_context * ccontext =
( struct create_context * ) iov [ n_iov - 1 ] . iov_base ;
ccontext - > Next =
cpu_to_le32 ( iov [ n_iov - 1 ] . iov_len ) ;
}
rc = add_posix_context ( iov , & n_iov , oparms - > mode ) ;
2018-08-08 08:07:46 +03:00
if ( rc )
2018-06-01 03:16:54 +03:00
return rc ;
}
smb3: allow previous versions to be mounted with snapshot= mount parm
mounting with the "snapshots=" mount parm allows a read-only
view of a previous version of a file system (see MS-SMB2
and "timewarp" tokens, section 2.2.13.2.6) based on the timestamp
passed in on the snapshots mount parm.
Add processing to optionally send this create context.
Example output:
/mnt1 is mounted with "snapshots=..." and will see an earlier
version of the directory, with three fewer files than /mnt2
the current version of the directory.
root@Ubuntu-17-Virtual-Machine:~/cifs-2.6# cat /proc/mounts | grep cifs
//172.22.149.186/public /mnt1 cifs
ro,relatime,vers=default,cache=strict,username=smfrench,uid=0,noforceuid,gid=0,noforcegid,addr=172.22.149.186,file_mode=0755,dir_mode=0755,soft,nounix,mapposix,rsize=1048576,wsize=1048576,echo_interval=60,snapshot=131748608570000000,actimeo=1
//172.22.149.186/public /mnt2 cifs
rw,relatime,vers=default,cache=strict,username=smfrench,uid=0,noforceuid,gid=0,noforcegid,addr=172.22.149.186,file_mode=0755,dir_mode=0755,soft,nounix,mapposix,rsize=1048576,wsize=1048576,echo_interval=60,actimeo=1
root@Ubuntu-17-Virtual-Machine:~/cifs-2.6# ls /mnt1
EmptyDir newerdir
root@Ubuntu-17-Virtual-Machine:~/cifs-2.6# ls /mnt1/newerdir
root@Ubuntu-17-Virtual-Machine:~/cifs-2.6# ls /mnt2
EmptyDir file newerdir newestdir timestamp-trace.cap
root@Ubuntu-17-Virtual-Machine:~/cifs-2.6# ls /mnt2/newerdir
new-file-not-in-snapshot
Snapshots are extremely useful for comparing previous versions of files or directories,
and recovering from data corruptions or mistakes.
Signed-off-by: Steve French <stfrench@microsoft.com>
Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com>
2018-08-10 10:25:06 +03:00
if ( tcon - > snapshot_time ) {
cifs_dbg ( FYI , " adding snapshot context \n " ) ;
if ( n_iov > 2 ) {
struct create_context * ccontext =
( struct create_context * ) iov [ n_iov - 1 ] . iov_base ;
ccontext - > Next =
cpu_to_le32 ( iov [ n_iov - 1 ] . iov_len ) ;
}
rc = add_twarp_context ( iov , & n_iov , tcon - > snapshot_time ) ;
if ( rc )
return rc ;
}
2018-08-08 08:07:46 +03:00
rqst - > rq_nvec = n_iov ;
return 0 ;
}
/* rq_iov[0] is the request and is released by cifs_small_buf_release().
* All other vectors are freed by kfree ( ) .
*/
void
SMB2_open_free ( struct smb_rqst * rqst )
{
int i ;
2018-10-24 04:50:33 +03:00
if ( rqst & & rqst - > rq_iov ) {
cifs_small_buf_release ( rqst - > rq_iov [ 0 ] . iov_base ) ;
for ( i = 1 ; i < rqst - > rq_nvec ; i + + )
if ( rqst - > rq_iov [ i ] . iov_base ! = smb2_padding )
kfree ( rqst - > rq_iov [ i ] . iov_base ) ;
}
2018-08-08 08:07:46 +03:00
}
int
SMB2_open ( const unsigned int xid , struct cifs_open_parms * oparms , __le16 * path ,
__u8 * oplock , struct smb2_file_all_info * buf ,
struct kvec * err_iov , int * buftype )
{
struct smb_rqst rqst ;
struct smb2_create_rsp * rsp = NULL ;
struct TCP_Server_Info * server ;
struct cifs_tcon * tcon = oparms - > tcon ;
struct cifs_ses * ses = tcon - > ses ;
2018-08-21 04:49:21 +03:00
struct kvec iov [ SMB2_CREATE_IOV_SIZE ] ;
2018-08-08 08:07:46 +03:00
struct kvec rsp_iov = { NULL , 0 } ;
2018-10-03 22:51:21 +03:00
int resp_buftype = CIFS_NO_BUFFER ;
2018-08-08 08:07:46 +03:00
int rc = 0 ;
int flags = 0 ;
cifs_dbg ( FYI , " create/open \n " ) ;
if ( ses & & ( ses - > server ) )
server = ses - > server ;
else
return - EIO ;
if ( smb3_encryption_required ( tcon ) )
flags | = CIFS_TRANSFORM_REQ ;
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
2018-08-08 08:07:46 +03:00
memset ( & iov , 0 , sizeof ( iov ) ) ;
2018-06-12 01:00:59 +03:00
rqst . rq_iov = iov ;
2018-08-21 04:49:21 +03:00
rqst . rq_nvec = SMB2_CREATE_IOV_SIZE ;
2018-08-08 08:07:46 +03:00
rc = SMB2_open_init ( tcon , & rqst , oplock , oparms , path ) ;
if ( rc )
goto creat_exit ;
2018-06-12 01:00:59 +03:00
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags ,
2017-11-20 03:24:38 +03:00
& rsp_iov ) ;
2016-10-25 21:38:47 +03:00
rsp = ( struct smb2_create_rsp * ) rsp_iov . iov_base ;
2011-12-26 22:58:46 +04:00
if ( rc ! = 0 ) {
cifs_stats_fail_inc ( tcon , SMB2_CREATE_HE ) ;
2018-04-13 02:03:19 +03:00
if ( err_iov & & rsp ) {
* err_iov = rsp_iov ;
2018-06-08 06:21:18 +03:00
* buftype = resp_buftype ;
2018-04-13 02:03:19 +03:00
resp_buftype = CIFS_NO_BUFFER ;
rsp = NULL ;
}
2018-05-31 05:42:34 +03:00
trace_smb3_open_err ( xid , tcon - > tid , ses - > Suid ,
oparms - > create_options , oparms - > desired_access , rc ) ;
2011-12-26 22:58:46 +04:00
goto creat_exit ;
2018-05-31 05:42:34 +03:00
} else
trace_smb3_open_done ( xid , rsp - > PersistentFileId , tcon - > tid ,
ses - > Suid , oparms - > create_options ,
oparms - > desired_access ) ;
2011-12-26 22:58:46 +04:00
2018-10-20 01:14:32 +03:00
atomic_inc ( & tcon - > num_remote_opens ) ;
2013-07-09 18:20:30 +04:00
oparms - > fid - > persistent_fid = rsp - > PersistentFileId ;
oparms - > fid - > volatile_fid = rsp - > VolatileFileId ;
2018-10-31 03:50:31 +03:00
# ifdef CONFIG_CIFS_DEBUG2
oparms - > fid - > mid = le64_to_cpu ( rsp - > sync_hdr . MessageId ) ;
# endif /* CIFS_DEBUG2 */
2012-09-19 03:20:26 +04:00
if ( buf ) {
memcpy ( buf , & rsp - > CreationTime , 32 ) ;
buf - > AllocationSize = rsp - > AllocationSize ;
buf - > EndOfFile = rsp - > EndofFile ;
buf - > Attributes = rsp - > FileAttributes ;
buf - > NumberOfLinks = cpu_to_le32 ( 1 ) ;
buf - > DeletePending = 0 ;
}
2012-09-19 03:20:33 +04:00
2012-09-19 17:22:44 +04:00
if ( rsp - > OplockLevel = = SMB2_OPLOCK_LEVEL_LEASE )
2018-04-26 17:10:18 +03:00
* oplock = parse_lease_state ( server , rsp , & oparms - > fid - > epoch ,
oparms - > fid - > lease_key ) ;
2012-09-19 17:22:44 +04:00
else
* oplock = rsp - > OplockLevel ;
2011-12-26 22:58:46 +04:00
creat_exit :
2018-08-08 08:07:46 +03:00
SMB2_open_free ( & rqst ) ;
2011-12-26 22:58:46 +04:00
free_rsp_buf ( resp_buftype , rsp ) ;
return rc ;
}
2013-06-25 09:20:49 +04:00
/*
* SMB2 IOCTL is used for both IOCTLs and FSCTLs
*/
int
SMB2_ioctl ( const unsigned int xid , struct cifs_tcon * tcon , u64 persistent_fid ,
2018-01-24 15:46:11 +03:00
u64 volatile_fid , u32 opcode , bool is_fsctl ,
2017-02-28 17:08:41 +03:00
char * in_data , u32 indatalen ,
char * * out_data , u32 * plen /* returned data len */ )
2013-06-25 09:20:49 +04:00
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2013-06-25 09:20:49 +04:00
struct smb2_ioctl_req * req ;
struct smb2_ioctl_rsp * rsp ;
2015-03-27 03:47:02 +03:00
struct cifs_ses * ses ;
2013-06-25 09:20:49 +04:00
struct kvec iov [ 2 ] ;
2016-10-25 21:38:47 +03:00
struct kvec rsp_iov ;
2013-06-25 09:20:49 +04:00
int resp_buftype ;
2016-10-25 21:38:47 +03:00
int n_iov ;
2013-06-25 09:20:49 +04:00
int rc = 0 ;
2016-10-31 23:49:30 +03:00
int flags = 0 ;
2017-11-09 04:14:20 +03:00
unsigned int total_len ;
2013-06-25 09:20:49 +04:00
cifs_dbg ( FYI , " SMB2 IOCTL \n " ) ;
2014-08-12 06:05:25 +04:00
if ( out_data ! = NULL )
* out_data = NULL ;
2013-06-25 09:20:49 +04:00
/* zero out returned data len, in case of error */
if ( plen )
* plen = 0 ;
2015-03-27 03:47:02 +03:00
if ( tcon )
ses = tcon - > ses ;
else
return - EIO ;
2017-07-09 13:45:04 +03:00
if ( ! ses | | ! ( ses - > server ) )
2013-06-25 09:20:49 +04:00
return - EIO ;
2017-11-09 04:14:20 +03:00
rc = smb2_plain_req_init ( SMB2_IOCTL , tcon , ( void * * ) & req , & total_len ) ;
2013-06-25 09:20:49 +04:00
if ( rc )
return rc ;
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2013-06-25 09:20:49 +04:00
req - > CtlCode = cpu_to_le32 ( opcode ) ;
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
if ( indatalen ) {
req - > InputCount = cpu_to_le32 ( indatalen ) ;
/* do not set InputOffset if no input data */
req - > InputOffset =
2017-11-09 04:14:20 +03:00
cpu_to_le32 ( offsetof ( struct smb2_ioctl_req , Buffer ) ) ;
2013-06-25 09:20:49 +04:00
iov [ 1 ] . iov_base = in_data ;
iov [ 1 ] . iov_len = indatalen ;
2016-10-25 21:38:47 +03:00
n_iov = 2 ;
2013-06-25 09:20:49 +04:00
} else
2016-10-25 21:38:47 +03:00
n_iov = 1 ;
2013-06-25 09:20:49 +04:00
req - > OutputOffset = 0 ;
req - > OutputCount = 0 ; /* MBZ */
/*
* Could increase MaxOutputResponse , but that would require more
* than one credit . Windows typically sets this smaller , but for some
* ioctls it may be useful to allow server to send more . No point
* limiting what the server can send as long as fits in one credit
2017-05-04 05:12:20 +03:00
* Unfortunately - we can not handle more than CIFS_MAX_MSG_SIZE
* ( by default , note that it can be overridden to make max larger )
* in responses ( except for read responses which can be bigger .
* We may want to bump this limit up
2013-06-25 09:20:49 +04:00
*/
2017-05-04 05:12:20 +03:00
req - > MaxOutputResponse = cpu_to_le32 ( CIFSMaxBufSize ) ;
2013-06-25 09:20:49 +04:00
if ( is_fsctl )
req - > Flags = cpu_to_le32 ( SMB2_0_IOCTL_IS_FSCTL ) ;
else
req - > Flags = 0 ;
iov [ 0 ] . iov_base = ( char * ) req ;
2013-10-14 09:44:19 +04:00
/*
* If no input data , the size of ioctl struct in
* protocol spec still includes a 1 byte data buffer ,
* but if input data passed to ioctl , we do not
* want to double count this , so we do not send
* the dummy one byte of data in iovec [ 0 ] if sending
2017-11-09 04:14:20 +03:00
* input data ( in iovec [ 1 ] ) .
2013-10-14 09:44:19 +04:00
*/
if ( indatalen ) {
2017-11-09 04:14:20 +03:00
iov [ 0 ] . iov_len = total_len - 1 ;
2013-10-14 09:44:19 +04:00
} else
2017-11-09 04:14:20 +03:00
iov [ 0 ] . iov_len = total_len ;
2013-10-14 09:44:19 +04:00
2017-10-25 23:58:31 +03:00
/* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */
if ( opcode = = FSCTL_VALIDATE_NEGOTIATE_INFO )
2017-11-09 04:14:20 +03:00
req - > sync_hdr . Flags | = SMB2_FLAGS_SIGNED ;
2013-06-25 09:20:49 +04:00
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = n_iov ;
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags ,
2017-11-09 04:14:20 +03:00
& rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( req ) ;
rsp = ( struct smb2_ioctl_rsp * ) rsp_iov . iov_base ;
2013-06-25 09:20:49 +04:00
2018-05-18 05:16:55 +03:00
if ( rc ! = 0 )
trace_smb3_fsctl_err ( xid , persistent_fid , tcon - > tid ,
ses - > Suid , 0 , opcode , rc ) ;
2013-11-17 04:05:28 +04:00
if ( ( rc ! = 0 ) & & ( rc ! = - EINVAL ) ) {
2015-03-27 03:47:02 +03:00
cifs_stats_fail_inc ( tcon , SMB2_IOCTL_HE ) ;
2013-06-25 09:20:49 +04:00
goto ioctl_exit ;
2013-11-17 04:05:28 +04:00
} else if ( rc = = - EINVAL ) {
if ( ( opcode ! = FSCTL_SRV_COPYCHUNK_WRITE ) & &
( opcode ! = FSCTL_SRV_COPYCHUNK ) ) {
2015-03-27 03:47:02 +03:00
cifs_stats_fail_inc ( tcon , SMB2_IOCTL_HE ) ;
2013-11-17 04:05:28 +04:00
goto ioctl_exit ;
}
2013-06-25 09:20:49 +04:00
}
/* check if caller wants to look at return data or just return rc */
if ( ( plen = = NULL ) | | ( out_data = = NULL ) )
goto ioctl_exit ;
* plen = le32_to_cpu ( rsp - > OutputCount ) ;
/* We check for obvious errors in the output buffer length and offset */
if ( * plen = = 0 )
goto ioctl_exit ; /* server returned no data */
2018-09-10 14:12:07 +03:00
else if ( * plen > rsp_iov . iov_len | | * plen > 0xFF00 ) {
2013-06-25 09:20:49 +04:00
cifs_dbg ( VFS , " srv returned invalid ioctl length: %d \n " , * plen ) ;
* plen = 0 ;
rc = - EIO ;
goto ioctl_exit ;
}
2018-09-10 14:12:07 +03:00
if ( rsp_iov . iov_len - * plen < le32_to_cpu ( rsp - > OutputOffset ) ) {
2013-06-25 09:20:49 +04:00
cifs_dbg ( VFS , " Malformed ioctl resp: len %d offset %d \n " , * plen ,
le32_to_cpu ( rsp - > OutputOffset ) ) ;
* plen = 0 ;
rc = - EIO ;
goto ioctl_exit ;
}
2018-09-10 04:33:06 +03:00
* out_data = kmemdup ( ( char * ) rsp + le32_to_cpu ( rsp - > OutputOffset ) ,
* plen , GFP_KERNEL ) ;
2013-06-25 09:20:49 +04:00
if ( * out_data = = NULL ) {
rc = - ENOMEM ;
goto ioctl_exit ;
}
ioctl_exit :
free_rsp_buf ( resp_buftype , rsp ) ;
return rc ;
}
2013-10-15 00:31:32 +04:00
/*
* Individual callers to ioctl worker function follow
*/
int
SMB2_set_compression ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid )
{
int rc ;
struct compress_ioctl fsctl_input ;
char * ret_data = NULL ;
fsctl_input . CompressionState =
2014-12-11 02:41:15 +03:00
cpu_to_le16 ( COMPRESSION_FORMAT_DEFAULT ) ;
2013-10-15 00:31:32 +04:00
rc = SMB2_ioctl ( xid , tcon , persistent_fid , volatile_fid ,
FSCTL_SET_COMPRESSION , true /* is_fsctl */ ,
( char * ) & fsctl_input /* data input */ ,
2 /* in data len */ , & ret_data /* out data */ , NULL ) ;
cifs_dbg ( FYI , " set compression rc %d \n " , rc ) ;
return rc ;
}
2018-08-01 02:26:16 +03:00
int
SMB2_close_init ( struct cifs_tcon * tcon , struct smb_rqst * rqst ,
u64 persistent_fid , u64 volatile_fid )
{
struct smb2_close_req * req ;
struct kvec * iov = rqst - > rq_iov ;
unsigned int total_len ;
int rc ;
rc = smb2_plain_req_init ( SMB2_CLOSE , tcon , ( void * * ) & req , & total_len ) ;
if ( rc )
return rc ;
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
iov [ 0 ] . iov_base = ( char * ) req ;
iov [ 0 ] . iov_len = total_len ;
return 0 ;
}
void
SMB2_close_free ( struct smb_rqst * rqst )
{
2018-10-24 04:50:33 +03:00
if ( rqst & & rqst - > rq_iov )
cifs_small_buf_release ( rqst - > rq_iov [ 0 ] . iov_base ) ; /* request */
2018-08-01 02:26:16 +03:00
}
2011-12-26 22:58:46 +04:00
int
2018-04-26 17:50:49 +03:00
SMB2_close_flags ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , int flags )
2011-12-26 22:58:46 +04:00
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2018-08-01 02:26:16 +03:00
struct smb2_close_rsp * rsp = NULL ;
2011-12-26 22:58:46 +04:00
struct cifs_ses * ses = tcon - > ses ;
struct kvec iov [ 1 ] ;
2016-10-25 21:38:47 +03:00
struct kvec rsp_iov ;
2018-10-03 22:51:21 +03:00
int resp_buftype = CIFS_NO_BUFFER ;
2011-12-26 22:58:46 +04:00
int rc = 0 ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Close \n " ) ;
2011-12-26 22:58:46 +04:00
2017-07-09 13:45:04 +03:00
if ( ! ses | | ! ( ses - > server ) )
2011-12-26 22:58:46 +04:00
return - EIO ;
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
2018-08-01 02:26:16 +03:00
memset ( & iov , 0 , sizeof ( iov ) ) ;
2018-06-12 01:00:59 +03:00
rqst . rq_iov = iov ;
rqst . rq_nvec = 1 ;
2018-08-01 02:26:16 +03:00
rc = SMB2_close_init ( tcon , & rqst , persistent_fid , volatile_fid ) ;
if ( rc )
goto close_exit ;
2018-06-12 01:00:59 +03:00
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
rsp = ( struct smb2_close_rsp * ) rsp_iov . iov_base ;
2011-12-26 22:58:46 +04:00
if ( rc ! = 0 ) {
2014-08-20 14:39:59 +04:00
cifs_stats_fail_inc ( tcon , SMB2_CLOSE_HE ) ;
2018-05-18 05:16:55 +03:00
trace_smb3_close_err ( xid , persistent_fid , tcon - > tid , ses - > Suid ,
rc ) ;
2011-12-26 22:58:46 +04:00
goto close_exit ;
}
2018-10-20 01:14:32 +03:00
atomic_dec ( & tcon - > num_remote_opens ) ;
2011-12-26 22:58:46 +04:00
/* BB FIXME - decode close response, update inode for caching */
close_exit :
2018-08-01 02:26:16 +03:00
SMB2_close_free ( & rqst ) ;
2011-12-26 22:58:46 +04:00
free_rsp_buf ( resp_buftype , rsp ) ;
return rc ;
}
2011-12-29 17:06:33 +04:00
2018-04-26 17:50:49 +03:00
int
SMB2_close ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid )
{
return SMB2_close_flags ( xid , tcon , persistent_fid , volatile_fid , 0 ) ;
}
2018-08-08 08:07:49 +03:00
int
smb2_validate_iov ( unsigned int offset , unsigned int buffer_length ,
struct kvec * iov , unsigned int min_buf_size )
2011-12-29 17:06:33 +04:00
{
2018-04-09 11:06:30 +03:00
unsigned int smb_len = iov - > iov_len ;
2018-06-01 03:53:07 +03:00
char * end_of_smb = smb_len + ( char * ) iov - > iov_base ;
char * begin_of_buf = offset + ( char * ) iov - > iov_base ;
2011-12-29 17:06:33 +04:00
char * end_of_buf = begin_of_buf + buffer_length ;
if ( buffer_length < min_buf_size ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " buffer length %d smaller than minimum size %d \n " ,
buffer_length , min_buf_size ) ;
2011-12-29 17:06:33 +04:00
return - EINVAL ;
}
/* check if beyond RFC1001 maximum length */
if ( ( smb_len > 0x7FFFFF ) | | ( buffer_length > 0x7FFFFF ) ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " buffer length %d or smb length %d too large \n " ,
buffer_length , smb_len ) ;
2011-12-29 17:06:33 +04:00
return - EINVAL ;
}
if ( ( begin_of_buf > end_of_smb ) | | ( end_of_buf > end_of_smb ) ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " illegal server response, bad offset to data \n " ) ;
2011-12-29 17:06:33 +04:00
return - EINVAL ;
}
return 0 ;
}
/*
* If SMB buffer fields are valid , copy into temporary buffer to hold result .
* Caller must free buffer .
*/
2018-09-03 06:33:41 +03:00
int
smb2_validate_and_copy_iov ( unsigned int offset , unsigned int buffer_length ,
struct kvec * iov , unsigned int minbufsize ,
char * data )
2011-12-29 17:06:33 +04:00
{
2018-06-01 03:53:07 +03:00
char * begin_of_buf = offset + ( char * ) iov - > iov_base ;
2011-12-29 17:06:33 +04:00
int rc ;
if ( ! data )
return - EINVAL ;
2018-08-08 08:07:49 +03:00
rc = smb2_validate_iov ( offset , buffer_length , iov , minbufsize ) ;
2011-12-29 17:06:33 +04:00
if ( rc )
return rc ;
memcpy ( data , begin_of_buf , buffer_length ) ;
return 0 ;
}
2018-08-01 02:26:17 +03:00
int
SMB2_query_info_init ( struct cifs_tcon * tcon , struct smb_rqst * rqst ,
u64 persistent_fid , u64 volatile_fid ,
u8 info_class , u8 info_type , u32 additional_info ,
2018-10-08 03:19:58 +03:00
size_t output_len , size_t input_len , void * input )
2011-12-29 17:06:33 +04:00
{
struct smb2_query_info_req * req ;
2018-08-01 02:26:17 +03:00
struct kvec * iov = rqst - > rq_iov ;
2017-11-20 03:24:46 +03:00
unsigned int total_len ;
2018-08-01 02:26:17 +03:00
int rc ;
2011-12-29 17:06:33 +04:00
2017-11-20 03:24:46 +03:00
rc = smb2_plain_req_init ( SMB2_QUERY_INFO , tcon , ( void * * ) & req ,
& total_len ) ;
2011-12-29 17:06:33 +04:00
if ( rc )
return rc ;
2017-06-23 06:51:31 +03:00
req - > InfoType = info_type ;
2012-09-19 03:20:26 +04:00
req - > FileInfoClass = info_class ;
2011-12-29 17:06:33 +04:00
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
2017-06-23 06:51:31 +03:00
req - > AdditionalInformation = cpu_to_le32 ( additional_info ) ;
2017-10-17 15:47:17 +03:00
2012-09-19 03:20:26 +04:00
req - > OutputBufferLength = cpu_to_le32 ( output_len ) ;
2018-10-08 03:19:58 +03:00
if ( input_len ) {
req - > InputBufferLength = cpu_to_le32 ( input_len ) ;
/* total_len for smb query request never close to le16 max */
req - > InputBufferOffset = cpu_to_le16 ( total_len - 1 ) ;
memcpy ( req - > Buffer , input , input_len ) ;
}
2011-12-29 17:06:33 +04:00
iov [ 0 ] . iov_base = ( char * ) req ;
2017-11-20 03:24:46 +03:00
/* 1 for Buffer */
2018-10-08 03:19:58 +03:00
iov [ 0 ] . iov_len = total_len - 1 + input_len ;
2018-08-01 02:26:17 +03:00
return 0 ;
}
void
SMB2_query_info_free ( struct smb_rqst * rqst )
{
2018-10-24 04:50:33 +03:00
if ( rqst & & rqst - > rq_iov )
cifs_small_buf_release ( rqst - > rq_iov [ 0 ] . iov_base ) ; /* request */
2018-08-01 02:26:17 +03:00
}
static int
query_info ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , u8 info_class , u8 info_type ,
u32 additional_info , size_t output_len , size_t min_len , void * * data ,
u32 * dlen )
{
struct smb_rqst rqst ;
struct smb2_query_info_rsp * rsp = NULL ;
struct kvec iov [ 1 ] ;
struct kvec rsp_iov ;
int rc = 0 ;
2018-10-03 22:51:21 +03:00
int resp_buftype = CIFS_NO_BUFFER ;
2018-08-01 02:26:17 +03:00
struct cifs_ses * ses = tcon - > ses ;
int flags = 0 ;
2019-01-16 19:28:59 +03:00
bool allocated = false ;
2018-08-01 02:26:17 +03:00
cifs_dbg ( FYI , " Query Info \n " ) ;
if ( ! ses | | ! ( ses - > server ) )
return - EIO ;
if ( smb3_encryption_required ( tcon ) )
flags | = CIFS_TRANSFORM_REQ ;
2011-12-29 17:06:33 +04:00
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
2018-08-01 02:26:17 +03:00
memset ( & iov , 0 , sizeof ( iov ) ) ;
2018-06-12 01:00:59 +03:00
rqst . rq_iov = iov ;
rqst . rq_nvec = 1 ;
2018-08-01 02:26:17 +03:00
rc = SMB2_query_info_init ( tcon , & rqst , persistent_fid , volatile_fid ,
info_class , info_type , additional_info ,
2018-10-08 03:19:58 +03:00
output_len , 0 , NULL ) ;
2018-08-01 02:26:17 +03:00
if ( rc )
goto qinf_exit ;
2018-06-12 01:00:59 +03:00
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
rsp = ( struct smb2_query_info_rsp * ) rsp_iov . iov_base ;
2012-09-19 16:03:26 +04:00
2011-12-29 17:06:33 +04:00
if ( rc ) {
cifs_stats_fail_inc ( tcon , SMB2_QUERY_INFO_HE ) ;
2018-05-18 05:16:55 +03:00
trace_smb3_query_info_err ( xid , persistent_fid , tcon - > tid ,
ses - > Suid , info_class , ( __u32 ) info_type , rc ) ;
2011-12-29 17:06:33 +04:00
goto qinf_exit ;
}
2017-06-23 06:51:31 +03:00
if ( dlen ) {
* dlen = le32_to_cpu ( rsp - > OutputBufferLength ) ;
if ( ! * data ) {
* data = kmalloc ( * dlen , GFP_KERNEL ) ;
if ( ! * data ) {
cifs_dbg ( VFS ,
" Error %d allocating memory for acl \n " ,
rc ) ;
* dlen = 0 ;
2019-01-16 19:28:59 +03:00
rc = - ENOMEM ;
2017-06-23 06:51:31 +03:00
goto qinf_exit ;
}
2019-01-16 19:28:59 +03:00
allocated = true ;
2017-06-23 06:51:31 +03:00
}
}
2018-09-03 06:33:41 +03:00
rc = smb2_validate_and_copy_iov ( le16_to_cpu ( rsp - > OutputBufferOffset ) ,
le32_to_cpu ( rsp - > OutputBufferLength ) ,
& rsp_iov , min_len , * data ) ;
2019-01-16 19:28:59 +03:00
if ( rc & & allocated ) {
kfree ( * data ) ;
* data = NULL ;
* dlen = 0 ;
}
2011-12-29 17:06:33 +04:00
qinf_exit :
2018-08-01 02:26:17 +03:00
SMB2_query_info_free ( & rqst ) ;
2011-12-29 17:06:33 +04:00
free_rsp_buf ( resp_buftype , rsp ) ;
return rc ;
}
2012-07-12 18:30:44 +04:00
2017-06-23 06:51:31 +03:00
int SMB2_query_info ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , struct smb2_file_all_info * data )
{
return query_info ( xid , tcon , persistent_fid , volatile_fid ,
FILE_ALL_INFORMATION , SMB2_O_INFO_FILE , 0 ,
sizeof ( struct smb2_file_all_info ) + PATH_MAX * 2 ,
sizeof ( struct smb2_file_all_info ) , ( void * * ) & data ,
NULL ) ;
}
2012-09-19 03:20:26 +04:00
int
2017-06-23 06:51:31 +03:00
SMB2_query_acl ( const unsigned int xid , struct cifs_tcon * tcon ,
2012-09-19 03:20:26 +04:00
u64 persistent_fid , u64 volatile_fid ,
2017-06-23 06:51:31 +03:00
void * * data , u32 * plen )
2012-09-19 03:20:26 +04:00
{
2017-06-23 06:51:31 +03:00
__u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO ;
* plen = 0 ;
2012-09-19 03:20:26 +04:00
return query_info ( xid , tcon , persistent_fid , volatile_fid ,
2017-06-23 06:51:31 +03:00
0 , SMB2_O_INFO_SECURITY , additional_info ,
2018-06-04 14:46:22 +03:00
SMB2_MAX_BUFFER_SIZE , MIN_SEC_DESC_LEN , data , plen ) ;
2012-09-19 03:20:26 +04:00
}
int
SMB2_get_srv_num ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , __le64 * uniqueid )
{
return query_info ( xid , tcon , persistent_fid , volatile_fid ,
2017-06-23 06:51:31 +03:00
FILE_INTERNAL_INFORMATION , SMB2_O_INFO_FILE , 0 ,
sizeof ( struct smb2_file_internal_info ) ,
2012-09-19 03:20:26 +04:00
sizeof ( struct smb2_file_internal_info ) ,
2017-06-23 06:51:31 +03:00
( void * * ) & uniqueid , NULL ) ;
2012-09-19 03:20:26 +04:00
}
2012-07-12 18:30:44 +04:00
/*
* This is a no - op for now . We ' re not really interested in the reply , but
* rather in the fact that the server sent one and that server - > lstrp
* gets updated .
*
* FIXME : maybe we should consider checking that the reply matches request ?
*/
static void
smb2_echo_callback ( struct mid_q_entry * mid )
{
struct TCP_Server_Info * server = mid - > callback_data ;
2016-10-25 01:33:04 +03:00
struct smb2_echo_rsp * rsp = ( struct smb2_echo_rsp * ) mid - > resp_buf ;
2012-07-12 18:30:44 +04:00
unsigned int credits_received = 1 ;
if ( mid - > mid_state = = MID_RESPONSE_RECEIVED )
2018-06-01 03:53:06 +03:00
credits_received = le16_to_cpu ( rsp - > sync_hdr . CreditRequest ) ;
2012-07-12 18:30:44 +04:00
DeleteMidQEntry ( mid ) ;
add_credits ( server , credits_received , CIFS_ECHO_OP ) ;
}
2016-11-04 21:50:31 +03:00
void smb2_reconnect_server ( struct work_struct * work )
{
struct TCP_Server_Info * server = container_of ( work ,
struct TCP_Server_Info , reconnect . work ) ;
struct cifs_ses * ses ;
struct cifs_tcon * tcon , * tcon2 ;
struct list_head tmp_list ;
int tcon_exist = false ;
2017-04-07 14:29:36 +03:00
int rc ;
int resched = false ;
2016-11-04 21:50:31 +03:00
/* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
mutex_lock ( & server - > reconnect_mutex ) ;
INIT_LIST_HEAD ( & tmp_list ) ;
cifs_dbg ( FYI , " Need negotiate, reconnecting tcons \n " ) ;
spin_lock ( & cifs_tcp_ses_lock ) ;
list_for_each_entry ( ses , & server - > smb_ses_list , smb_ses_list ) {
list_for_each_entry ( tcon , & ses - > tcon_list , tcon_list ) {
2016-11-29 22:31:23 +03:00
if ( tcon - > need_reconnect | | tcon - > need_reopen_files ) {
2016-11-04 21:50:31 +03:00
tcon - > tc_count + + ;
list_add_tail ( & tcon - > rlist , & tmp_list ) ;
tcon_exist = true ;
}
}
2018-01-24 15:46:10 +03:00
if ( ses - > tcon_ipc & & ses - > tcon_ipc - > need_reconnect ) {
list_add_tail ( & ses - > tcon_ipc - > rlist , & tmp_list ) ;
tcon_exist = true ;
}
2016-11-04 21:50:31 +03:00
}
/*
* Get the reference to server struct to be sure that the last call of
* cifs_put_tcon ( ) in the loop below won ' t release the server pointer .
*/
if ( tcon_exist )
server - > srv_count + + ;
spin_unlock ( & cifs_tcp_ses_lock ) ;
list_for_each_entry_safe ( tcon , tcon2 , & tmp_list , rlist ) {
2017-04-07 14:29:36 +03:00
rc = smb2_reconnect ( SMB2_INTERNAL_CMD , tcon ) ;
if ( ! rc )
2016-11-29 22:31:23 +03:00
cifs_reopen_persistent_handles ( tcon ) ;
2017-04-07 14:29:36 +03:00
else
resched = true ;
2016-11-04 21:50:31 +03:00
list_del_init ( & tcon - > rlist ) ;
cifs_put_tcon ( tcon ) ;
}
cifs_dbg ( FYI , " Reconnecting tcons finished \n " ) ;
2017-04-07 14:29:36 +03:00
if ( resched )
queue_delayed_work ( cifsiod_wq , & server - > reconnect , 2 * HZ ) ;
2016-11-04 21:50:31 +03:00
mutex_unlock ( & server - > reconnect_mutex ) ;
/* now we can safely release srv struct */
if ( tcon_exist )
cifs_put_tcp_session ( server , 1 ) ;
}
2012-07-12 18:30:44 +04:00
int
SMB2_echo ( struct TCP_Server_Info * server )
{
struct smb2_echo_req * req ;
int rc = 0 ;
2018-06-12 01:00:58 +03:00
struct kvec iov [ 1 ] ;
2016-11-24 02:14:57 +03:00
struct smb_rqst rqst = { . rq_iov = iov ,
2018-06-12 01:00:58 +03:00
. rq_nvec = 1 } ;
2017-11-09 04:14:21 +03:00
unsigned int total_len ;
2012-07-12 18:30:44 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " In echo request \n " ) ;
2012-07-12 18:30:44 +04:00
2016-06-23 04:12:05 +03:00
if ( server - > tcpStatus = = CifsNeedNegotiate ) {
2016-11-04 21:50:31 +03:00
/* No need to send echo on newly established connections */
queue_delayed_work ( cifsiod_wq , & server - > reconnect , 0 ) ;
return rc ;
2016-06-23 04:12:05 +03:00
}
2017-11-09 04:14:21 +03:00
rc = smb2_plain_req_init ( SMB2_ECHO , NULL , ( void * * ) & req , & total_len ) ;
2012-07-12 18:30:44 +04:00
if ( rc )
return rc ;
2017-11-09 04:14:21 +03:00
req - > sync_hdr . CreditRequest = cpu_to_le16 ( 1 ) ;
2012-07-12 18:30:44 +04:00
2018-06-12 01:00:58 +03:00
iov [ 0 ] . iov_len = total_len ;
iov [ 0 ] . iov_base = ( char * ) req ;
2012-07-12 18:30:44 +04:00
2016-11-17 01:06:17 +03:00
rc = cifs_call_async ( server , & rqst , NULL , smb2_echo_callback , NULL ,
server , CIFS_ECHO_OP ) ;
2012-07-12 18:30:44 +04:00
if ( rc )
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Echo request failed: %d \n " , rc ) ;
2012-07-12 18:30:44 +04:00
cifs_small_buf_release ( req ) ;
return rc ;
}
2012-09-19 03:20:28 +04:00
int
SMB2_flush ( const unsigned int xid , struct cifs_tcon * tcon , u64 persistent_fid ,
u64 volatile_fid )
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2012-09-19 03:20:28 +04:00
struct smb2_flush_req * req ;
struct cifs_ses * ses = tcon - > ses ;
struct kvec iov [ 1 ] ;
2016-10-25 21:38:47 +03:00
struct kvec rsp_iov ;
2012-09-19 03:20:28 +04:00
int resp_buftype ;
int rc = 0 ;
2016-10-31 23:49:30 +03:00
int flags = 0 ;
2017-11-20 03:24:39 +03:00
unsigned int total_len ;
2012-09-19 03:20:28 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Flush \n " ) ;
2012-09-19 03:20:28 +04:00
2017-07-09 13:45:04 +03:00
if ( ! ses | | ! ( ses - > server ) )
2012-09-19 03:20:28 +04:00
return - EIO ;
2017-11-20 03:24:39 +03:00
rc = smb2_plain_req_init ( SMB2_FLUSH , tcon , ( void * * ) & req , & total_len ) ;
2012-09-19 03:20:28 +04:00
if ( rc )
return rc ;
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2012-09-19 03:20:28 +04:00
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
iov [ 0 ] . iov_base = ( char * ) req ;
2017-11-20 03:24:39 +03:00
iov [ 0 ] . iov_len = total_len ;
2012-09-19 03:20:28 +04:00
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = 1 ;
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( req ) ;
2012-09-19 03:20:28 +04:00
2018-05-18 05:16:55 +03:00
if ( rc ! = 0 ) {
2012-09-19 03:20:28 +04:00
cifs_stats_fail_inc ( tcon , SMB2_FLUSH_HE ) ;
2018-05-18 05:16:55 +03:00
trace_smb3_flush_err ( xid , persistent_fid , tcon - > tid , ses - > Suid ,
rc ) ;
}
2012-09-19 03:20:28 +04:00
2016-10-25 21:38:47 +03:00
free_rsp_buf ( resp_buftype , rsp_iov . iov_base ) ;
2012-09-19 03:20:28 +04:00
return rc ;
}
2012-09-19 03:20:29 +04:00
/*
* To form a chain of read requests , any read requests after the first should
* have the end_of_chain boolean set to true .
*/
static int
2016-11-24 02:14:57 +03:00
smb2_new_read_req ( void * * buf , unsigned int * total_len ,
2017-11-07 11:54:53 +03:00
struct cifs_io_parms * io_parms , struct cifs_readdata * rdata ,
unsigned int remaining_bytes , int request_type )
2012-09-19 03:20:29 +04:00
{
int rc = - EACCES ;
2016-11-24 02:31:54 +03:00
struct smb2_read_plain_req * req = NULL ;
2016-10-25 01:33:04 +03:00
struct smb2_sync_hdr * shdr ;
2017-11-07 11:54:53 +03:00
struct TCP_Server_Info * server ;
2012-09-19 03:20:29 +04:00
2016-11-24 02:31:54 +03:00
rc = smb2_plain_req_init ( SMB2_READ , io_parms - > tcon , ( void * * ) & req ,
total_len ) ;
2012-09-19 03:20:29 +04:00
if ( rc )
return rc ;
2017-11-07 11:54:53 +03:00
server = io_parms - > tcon - > ses - > server ;
if ( server = = NULL )
2012-09-19 03:20:29 +04:00
return - ECONNABORTED ;
2016-11-24 02:31:54 +03:00
shdr = & req - > sync_hdr ;
2016-10-25 01:33:04 +03:00
shdr - > ProcessId = cpu_to_le32 ( io_parms - > pid ) ;
2012-09-19 03:20:29 +04:00
req - > PersistentFileId = io_parms - > persistent_fid ;
req - > VolatileFileId = io_parms - > volatile_fid ;
req - > ReadChannelInfoOffset = 0 ; /* reserved */
req - > ReadChannelInfoLength = 0 ; /* reserved */
req - > Channel = 0 ; /* reserved */
req - > MinimumCount = 0 ;
req - > Length = cpu_to_le32 ( io_parms - > length ) ;
req - > Offset = cpu_to_le64 ( io_parms - > offset ) ;
2017-11-23 03:38:47 +03:00
# ifdef CONFIG_CIFS_SMB_DIRECT
/*
* If we want to do a RDMA write , fill in and append
* smbd_buffer_descriptor_v1 to the end of read request
*/
2018-04-17 22:17:08 +03:00
if ( server - > rdma & & rdata & & ! server - > sign & &
2017-11-23 03:38:47 +03:00
rdata - > bytes > = server - > smbd_conn - > rdma_readwrite_threshold ) {
struct smbd_buffer_descriptor_v1 * v1 ;
bool need_invalidate =
io_parms - > tcon - > ses - > server - > dialect = = SMB30_PROT_ID ;
rdata - > mr = smbd_register_mr (
server - > smbd_conn , rdata - > pages ,
2018-05-30 22:48:02 +03:00
rdata - > nr_pages , rdata - > page_offset ,
rdata - > tailsz , true , need_invalidate ) ;
2017-11-23 03:38:47 +03:00
if ( ! rdata - > mr )
return - ENOBUFS ;
req - > Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE ;
if ( need_invalidate )
req - > Channel = SMB2_CHANNEL_RDMA_V1 ;
req - > ReadChannelInfoOffset =
2018-01-25 08:07:41 +03:00
cpu_to_le16 ( offsetof ( struct smb2_read_plain_req , Buffer ) ) ;
2017-11-23 03:38:47 +03:00
req - > ReadChannelInfoLength =
2018-01-25 08:07:41 +03:00
cpu_to_le16 ( sizeof ( struct smbd_buffer_descriptor_v1 ) ) ;
2017-11-23 03:38:47 +03:00
v1 = ( struct smbd_buffer_descriptor_v1 * ) & req - > Buffer [ 0 ] ;
2018-01-25 08:07:41 +03:00
v1 - > offset = cpu_to_le64 ( rdata - > mr - > mr - > iova ) ;
v1 - > token = cpu_to_le32 ( rdata - > mr - > mr - > rkey ) ;
v1 - > length = cpu_to_le32 ( rdata - > mr - > mr - > length ) ;
2017-11-23 03:38:47 +03:00
* total_len + = sizeof ( * v1 ) - 1 ;
}
# endif
2012-09-19 03:20:29 +04:00
if ( request_type & CHAINED_REQUEST ) {
if ( ! ( request_type & END_OF_CHAIN ) ) {
2016-11-24 02:31:54 +03:00
/* next 8-byte aligned request */
* total_len = DIV_ROUND_UP ( * total_len , 8 ) * 8 ;
shdr - > NextCommand = cpu_to_le32 ( * total_len ) ;
2012-09-19 03:20:29 +04:00
} else /* END_OF_CHAIN */
2016-10-25 01:33:04 +03:00
shdr - > NextCommand = 0 ;
2012-09-19 03:20:29 +04:00
if ( request_type & RELATED_REQUEST ) {
2016-10-25 01:33:04 +03:00
shdr - > Flags | = SMB2_FLAGS_RELATED_OPERATIONS ;
2012-09-19 03:20:29 +04:00
/*
* Related requests use info from previous read request
* in chain .
*/
2016-10-25 01:33:04 +03:00
shdr - > SessionId = 0xFFFFFFFF ;
shdr - > TreeId = 0xFFFFFFFF ;
2012-09-19 03:20:29 +04:00
req - > PersistentFileId = 0xFFFFFFFF ;
req - > VolatileFileId = 0xFFFFFFFF ;
}
}
if ( remaining_bytes > io_parms - > length )
req - > RemainingBytes = cpu_to_le32 ( remaining_bytes ) ;
else
req - > RemainingBytes = 0 ;
2016-11-24 02:14:57 +03:00
* buf = req ;
2012-09-19 03:20:29 +04:00
return rc ;
}
static void
smb2_readv_callback ( struct mid_q_entry * mid )
{
struct cifs_readdata * rdata = mid - > callback_data ;
struct cifs_tcon * tcon = tlink_tcon ( rdata - > cfile - > tlink ) ;
struct TCP_Server_Info * server = tcon - > ses - > server ;
2016-11-24 02:14:57 +03:00
struct smb2_sync_hdr * shdr =
2018-06-01 03:53:02 +03:00
( struct smb2_sync_hdr * ) rdata - > iov [ 0 ] . iov_base ;
2012-09-19 03:20:29 +04:00
unsigned int credits_received = 1 ;
2016-11-24 02:14:57 +03:00
struct smb_rqst rqst = { . rq_iov = rdata - > iov ,
. rq_nvec = 2 ,
2012-09-19 17:22:32 +04:00
. rq_pages = rdata - > pages ,
2018-05-30 22:47:55 +03:00
. rq_offset = rdata - > page_offset ,
2012-09-19 17:22:32 +04:00
. rq_npages = rdata - > nr_pages ,
. rq_pagesz = rdata - > pagesz ,
. rq_tailsz = rdata - > tailsz } ;
2012-09-19 03:20:29 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: mid=%llu state=%d result=%d bytes=%u \n " ,
__func__ , mid - > mid , mid - > mid_state , rdata - > result ,
rdata - > bytes ) ;
2012-09-19 03:20:29 +04:00
switch ( mid - > mid_state ) {
case MID_RESPONSE_RECEIVED :
2016-10-25 01:33:04 +03:00
credits_received = le16_to_cpu ( shdr - > CreditRequest ) ;
2012-09-19 03:20:29 +04:00
/* result already set, check signature */
2016-11-18 02:24:46 +03:00
if ( server - > sign & & ! mid - > decrypted ) {
2012-09-19 03:20:30 +04:00
int rc ;
2012-09-19 03:20:34 +04:00
rc = smb2_verify_signature ( & rqst , server ) ;
2012-09-19 03:20:30 +04:00
if ( rc )
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " SMB signature verification returned error = %d \n " ,
rc ) ;
2012-09-19 03:20:30 +04:00
}
2012-09-19 03:20:29 +04:00
/* FIXME: should this be counted toward the initiating task? */
2014-07-10 10:03:29 +04:00
task_io_account_read ( rdata - > got_bytes ) ;
cifs_stats_bytes_read ( tcon , rdata - > got_bytes ) ;
2012-09-19 03:20:29 +04:00
break ;
case MID_REQUEST_SUBMITTED :
case MID_RETRY_NEEDED :
rdata - > result = - EAGAIN ;
2014-07-10 11:31:48 +04:00
if ( server - > sign & & rdata - > got_bytes )
/* reset bytes number since we can not check a sign */
rdata - > got_bytes = 0 ;
/* FIXME: should this be counted toward the initiating task? */
task_io_account_read ( rdata - > got_bytes ) ;
cifs_stats_bytes_read ( tcon , rdata - > got_bytes ) ;
2012-09-19 03:20:29 +04:00
break ;
default :
if ( rdata - > result ! = - ENODATA )
rdata - > result = - EIO ;
}
2017-11-23 03:38:47 +03:00
# ifdef CONFIG_CIFS_SMB_DIRECT
/*
* If this rdata has a memmory registered , the MR can be freed
* MR needs to be freed as soon as I / O finishes to prevent deadlock
* because they have limited number and are used for future I / Os
*/
if ( rdata - > mr ) {
smbd_deregister_mr ( rdata - > mr ) ;
rdata - > mr = NULL ;
}
# endif
2012-09-19 03:20:29 +04:00
if ( rdata - > result )
cifs_stats_fail_inc ( tcon , SMB2_READ_HE ) ;
queue_work ( cifsiod_wq , & rdata - > work ) ;
DeleteMidQEntry ( mid ) ;
add_credits ( server , credits_received , 0 ) ;
}
2016-11-24 02:14:57 +03:00
/* smb2_async_readv - send an async read, and set up mid to handle result */
2012-09-19 03:20:29 +04:00
int
smb2_async_readv ( struct cifs_readdata * rdata )
{
2014-06-25 11:28:57 +04:00
int rc , flags = 0 ;
2016-10-25 01:33:04 +03:00
char * buf ;
struct smb2_sync_hdr * shdr ;
2012-09-19 03:20:29 +04:00
struct cifs_io_parms io_parms ;
2016-11-24 02:14:57 +03:00
struct smb_rqst rqst = { . rq_iov = rdata - > iov ,
2018-06-12 01:00:58 +03:00
. rq_nvec = 1 } ;
2014-06-25 11:28:57 +04:00
struct TCP_Server_Info * server ;
2016-11-24 02:14:57 +03:00
unsigned int total_len ;
2012-09-19 03:20:29 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " %s: offset=%llu bytes=%u \n " ,
__func__ , rdata - > offset , rdata - > bytes ) ;
2012-09-19 03:20:29 +04:00
io_parms . tcon = tlink_tcon ( rdata - > cfile - > tlink ) ;
io_parms . offset = rdata - > offset ;
io_parms . length = rdata - > bytes ;
io_parms . persistent_fid = rdata - > cfile - > fid . persistent_fid ;
io_parms . volatile_fid = rdata - > cfile - > fid . volatile_fid ;
io_parms . pid = rdata - > pid ;
2014-06-25 11:28:57 +04:00
server = io_parms . tcon - > ses - > server ;
2017-11-07 11:54:53 +03:00
rc = smb2_new_read_req (
( void * * ) & buf , & total_len , & io_parms , rdata , 0 , 0 ) ;
2014-06-25 11:28:57 +04:00
if ( rc ) {
if ( rc = = - EAGAIN & & rdata - > credits ) {
/* credits was reset by reconnect */
rdata - > credits = 0 ;
/* reduce in_flight value since we won't send the req */
spin_lock ( & server - > req_lock ) ;
server - > in_flight - - ;
spin_unlock ( & server - > req_lock ) ;
}
2012-09-19 03:20:29 +04:00
return rc ;
2014-06-25 11:28:57 +04:00
}
2012-09-19 03:20:29 +04:00
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( io_parms . tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2018-06-12 01:00:58 +03:00
rdata - > iov [ 0 ] . iov_base = buf ;
rdata - > iov [ 0 ] . iov_len = total_len ;
2016-11-24 02:31:54 +03:00
shdr = ( struct smb2_sync_hdr * ) buf ;
2012-09-19 03:20:29 +04:00
2014-06-25 11:28:57 +04:00
if ( rdata - > credits ) {
2016-10-25 01:33:04 +03:00
shdr - > CreditCharge = cpu_to_le16 ( DIV_ROUND_UP ( rdata - > bytes ,
2014-06-25 11:28:57 +04:00
SMB2_MAX_BUFFER_SIZE ) ) ;
2018-12-20 01:49:09 +03:00
shdr - > CreditRequest =
cpu_to_le16 ( le16_to_cpu ( shdr - > CreditCharge ) + 1 ) ;
2014-06-25 11:28:57 +04:00
spin_lock ( & server - > req_lock ) ;
server - > credits + = rdata - > credits -
2016-10-25 01:33:04 +03:00
le16_to_cpu ( shdr - > CreditCharge ) ;
2014-06-25 11:28:57 +04:00
spin_unlock ( & server - > req_lock ) ;
wake_up ( & server - > request_q ) ;
2018-12-20 01:49:09 +03:00
rdata - > credits = le16_to_cpu ( shdr - > CreditCharge ) ;
2016-10-31 23:49:30 +03:00
flags | = CIFS_HAS_CREDITS ;
2014-06-25 11:28:57 +04:00
}
2012-09-19 03:20:29 +04:00
kref_get ( & rdata - > refcount ) ;
2012-09-19 03:20:35 +04:00
rc = cifs_call_async ( io_parms . tcon - > ses - > server , & rqst ,
2012-09-19 03:20:29 +04:00
cifs_readv_receive , smb2_readv_callback ,
2016-11-18 02:24:46 +03:00
smb3_handle_read_data , rdata , flags ) ;
2012-09-19 16:03:26 +04:00
if ( rc ) {
2012-09-19 03:20:29 +04:00
kref_put ( & rdata - > refcount , cifs_readdata_release ) ;
2012-09-19 16:03:26 +04:00
cifs_stats_fail_inc ( io_parms . tcon , SMB2_READ_HE ) ;
2018-05-18 05:16:55 +03:00
trace_smb3_read_err ( rc , 0 /* xid */ , io_parms . persistent_fid ,
io_parms . tcon - > tid , io_parms . tcon - > ses - > Suid ,
io_parms . offset , io_parms . length ) ;
} else
trace_smb3_read_done ( 0 /* xid */ , io_parms . persistent_fid ,
io_parms . tcon - > tid , io_parms . tcon - > ses - > Suid ,
io_parms . offset , io_parms . length ) ;
2012-09-19 03:20:29 +04:00
cifs_small_buf_release ( buf ) ;
return rc ;
}
2012-09-19 03:20:29 +04:00
2012-09-19 03:20:30 +04:00
int
SMB2_read ( const unsigned int xid , struct cifs_io_parms * io_parms ,
unsigned int * nbytes , char * * buf , int * buf_type )
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2012-09-19 03:20:30 +04:00
int resp_buftype , rc = - EACCES ;
2016-11-24 02:31:54 +03:00
struct smb2_read_plain_req * req = NULL ;
2012-09-19 03:20:30 +04:00
struct smb2_read_rsp * rsp = NULL ;
2017-11-20 03:24:41 +03:00
struct kvec iov [ 1 ] ;
2016-10-25 21:38:47 +03:00
struct kvec rsp_iov ;
2016-11-24 02:14:57 +03:00
unsigned int total_len ;
2016-10-31 23:49:30 +03:00
int flags = CIFS_LOG_ERROR ;
struct cifs_ses * ses = io_parms - > tcon - > ses ;
2012-09-19 03:20:30 +04:00
* nbytes = 0 ;
2017-11-07 11:54:53 +03:00
rc = smb2_new_read_req ( ( void * * ) & req , & total_len , io_parms , NULL , 0 , 0 ) ;
2012-09-19 03:20:30 +04:00
if ( rc )
return rc ;
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( io_parms - > tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2017-11-20 03:24:41 +03:00
iov [ 0 ] . iov_base = ( char * ) req ;
iov [ 0 ] . iov_len = total_len ;
2016-11-24 02:31:54 +03:00
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = 1 ;
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags , & rsp_iov ) ;
2016-11-24 02:31:54 +03:00
cifs_small_buf_release ( req ) ;
2012-09-19 03:20:30 +04:00
2016-10-25 21:38:47 +03:00
rsp = ( struct smb2_read_rsp * ) rsp_iov . iov_base ;
2012-09-19 03:20:30 +04:00
2017-11-21 01:36:33 +03:00
if ( rc ) {
if ( rc ! = - ENODATA ) {
cifs_stats_fail_inc ( io_parms - > tcon , SMB2_READ_HE ) ;
cifs_dbg ( VFS , " Send error in read = %d \n " , rc ) ;
}
2018-05-18 05:16:55 +03:00
trace_smb3_read_err ( rc , xid , req - > PersistentFileId ,
io_parms - > tcon - > tid , ses - > Suid ,
io_parms - > offset , io_parms - > length ) ;
2016-10-25 21:38:47 +03:00
free_rsp_buf ( resp_buftype , rsp_iov . iov_base ) ;
2017-11-21 01:36:33 +03:00
return rc = = - ENODATA ? 0 : rc ;
2018-05-18 05:16:55 +03:00
} else
trace_smb3_read_done ( xid , req - > PersistentFileId ,
io_parms - > tcon - > tid , ses - > Suid ,
io_parms - > offset , io_parms - > length ) ;
2012-09-19 03:20:30 +04:00
2017-11-21 01:36:33 +03:00
* nbytes = le32_to_cpu ( rsp - > DataLength ) ;
if ( ( * nbytes > CIFS_MAX_MSGSIZE ) | |
( * nbytes > io_parms - > length ) ) {
cifs_dbg ( FYI , " bad length %d for count %d \n " ,
* nbytes , io_parms - > length ) ;
rc = - EIO ;
* nbytes = 0 ;
2012-09-19 03:20:30 +04:00
}
if ( * buf ) {
2018-06-01 03:53:02 +03:00
memcpy ( * buf , ( char * ) rsp + rsp - > DataOffset , * nbytes ) ;
2016-10-25 21:38:47 +03:00
free_rsp_buf ( resp_buftype , rsp_iov . iov_base ) ;
2012-09-19 03:20:30 +04:00
} else if ( resp_buftype ! = CIFS_NO_BUFFER ) {
2016-10-25 21:38:47 +03:00
* buf = rsp_iov . iov_base ;
2012-09-19 03:20:30 +04:00
if ( resp_buftype = = CIFS_SMALL_BUFFER )
* buf_type = CIFS_SMALL_BUFFER ;
else if ( resp_buftype = = CIFS_LARGE_BUFFER )
* buf_type = CIFS_LARGE_BUFFER ;
}
return rc ;
}
2012-09-19 03:20:29 +04:00
/*
* Check the mid_state and signature on received buffer ( if any ) , and queue the
* workqueue completion task .
*/
static void
smb2_writev_callback ( struct mid_q_entry * mid )
{
struct cifs_writedata * wdata = mid - > callback_data ;
struct cifs_tcon * tcon = tlink_tcon ( wdata - > cfile - > tlink ) ;
unsigned int written ;
struct smb2_write_rsp * rsp = ( struct smb2_write_rsp * ) mid - > resp_buf ;
unsigned int credits_received = 1 ;
switch ( mid - > mid_state ) {
case MID_RESPONSE_RECEIVED :
2018-06-01 03:53:06 +03:00
credits_received = le16_to_cpu ( rsp - > sync_hdr . CreditRequest ) ;
2012-09-19 03:20:29 +04:00
wdata - > result = smb2_check_receive ( mid , tcon - > ses - > server , 0 ) ;
if ( wdata - > result ! = 0 )
break ;
written = le32_to_cpu ( rsp - > DataLength ) ;
/*
* Mask off high 16 bits when bytes written as returned
* by the server is greater than bytes requested by the
* client . OS / 2 servers are known to set incorrect
* CountHigh values .
*/
if ( written > wdata - > bytes )
written & = 0xFFFF ;
if ( written < wdata - > bytes )
wdata - > result = - ENOSPC ;
else
wdata - > bytes = written ;
break ;
case MID_REQUEST_SUBMITTED :
case MID_RETRY_NEEDED :
wdata - > result = - EAGAIN ;
break ;
default :
wdata - > result = - EIO ;
break ;
}
2017-11-23 03:38:45 +03:00
# ifdef CONFIG_CIFS_SMB_DIRECT
/*
* If this wdata has a memory registered , the MR can be freed
* The number of MRs available is limited , it ' s important to recover
* used MR as soon as I / O is finished . Hold MR longer in the later
* I / O process can possibly result in I / O deadlock due to lack of MR
* to send request on I / O retry
*/
if ( wdata - > mr ) {
smbd_deregister_mr ( wdata - > mr ) ;
wdata - > mr = NULL ;
}
# endif
2012-09-19 03:20:29 +04:00
if ( wdata - > result )
cifs_stats_fail_inc ( tcon , SMB2_WRITE_HE ) ;
queue_work ( cifsiod_wq , & wdata - > work ) ;
DeleteMidQEntry ( mid ) ;
add_credits ( tcon - > ses - > server , credits_received , 0 ) ;
}
/* smb2_async_writev - send an async write, and set up mid to handle result */
int
2014-02-08 06:45:12 +04:00
smb2_async_writev ( struct cifs_writedata * wdata ,
void ( * release ) ( struct kref * kref ) )
2012-09-19 03:20:29 +04:00
{
2014-06-05 19:03:27 +04:00
int rc = - EACCES , flags = 0 ;
2012-09-19 03:20:29 +04:00
struct smb2_write_req * req = NULL ;
2016-10-25 01:33:04 +03:00
struct smb2_sync_hdr * shdr ;
2012-09-19 03:20:29 +04:00
struct cifs_tcon * tcon = tlink_tcon ( wdata - > cfile - > tlink ) ;
2014-06-05 19:03:27 +04:00
struct TCP_Server_Info * server = tcon - > ses - > server ;
2018-06-12 01:00:58 +03:00
struct kvec iov [ 1 ] ;
2016-11-24 02:14:57 +03:00
struct smb_rqst rqst = { } ;
2017-11-20 03:24:41 +03:00
unsigned int total_len ;
2012-09-19 03:20:29 +04:00
2017-11-20 03:24:41 +03:00
rc = smb2_plain_req_init ( SMB2_WRITE , tcon , ( void * * ) & req , & total_len ) ;
2014-06-05 19:03:27 +04:00
if ( rc ) {
if ( rc = = - EAGAIN & & wdata - > credits ) {
/* credits was reset by reconnect */
wdata - > credits = 0 ;
/* reduce in_flight value since we won't send the req */
spin_lock ( & server - > req_lock ) ;
server - > in_flight - - ;
spin_unlock ( & server - > req_lock ) ;
}
2012-09-19 03:20:29 +04:00
goto async_writev_out ;
2014-06-05 19:03:27 +04:00
}
2012-09-19 03:20:29 +04:00
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2017-11-20 03:24:41 +03:00
shdr = ( struct smb2_sync_hdr * ) req ;
2016-10-25 01:33:04 +03:00
shdr - > ProcessId = cpu_to_le32 ( wdata - > cfile - > pid ) ;
2012-09-19 03:20:29 +04:00
req - > PersistentFileId = wdata - > cfile - > fid . persistent_fid ;
req - > VolatileFileId = wdata - > cfile - > fid . volatile_fid ;
req - > WriteChannelInfoOffset = 0 ;
req - > WriteChannelInfoLength = 0 ;
req - > Channel = 0 ;
req - > Offset = cpu_to_le64 ( wdata - > offset ) ;
req - > DataOffset = cpu_to_le16 (
2017-11-20 03:24:41 +03:00
offsetof ( struct smb2_write_req , Buffer ) ) ;
2012-09-19 03:20:29 +04:00
req - > RemainingBytes = 0 ;
2017-11-23 03:38:45 +03:00
# ifdef CONFIG_CIFS_SMB_DIRECT
/*
* If we want to do a server RDMA read , fill in and append
* smbd_buffer_descriptor_v1 to the end of write request
*/
2018-04-17 22:17:08 +03:00
if ( server - > rdma & & ! server - > sign & & wdata - > bytes > =
2017-11-23 03:38:45 +03:00
server - > smbd_conn - > rdma_readwrite_threshold ) {
struct smbd_buffer_descriptor_v1 * v1 ;
bool need_invalidate = server - > dialect = = SMB30_PROT_ID ;
wdata - > mr = smbd_register_mr (
server - > smbd_conn , wdata - > pages ,
2018-05-30 22:48:02 +03:00
wdata - > nr_pages , wdata - > page_offset ,
wdata - > tailsz , false , need_invalidate ) ;
2017-11-23 03:38:45 +03:00
if ( ! wdata - > mr ) {
rc = - ENOBUFS ;
goto async_writev_out ;
}
req - > Length = 0 ;
req - > DataOffset = 0 ;
2018-05-30 22:48:02 +03:00
if ( wdata - > nr_pages > 1 )
req - > RemainingBytes =
cpu_to_le32 (
( wdata - > nr_pages - 1 ) * wdata - > pagesz -
wdata - > page_offset + wdata - > tailsz
) ;
else
req - > RemainingBytes = cpu_to_le32 ( wdata - > tailsz ) ;
2017-11-23 03:38:45 +03:00
req - > Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE ;
if ( need_invalidate )
req - > Channel = SMB2_CHANNEL_RDMA_V1 ;
req - > WriteChannelInfoOffset =
2018-01-25 08:07:41 +03:00
cpu_to_le16 ( offsetof ( struct smb2_write_req , Buffer ) ) ;
2017-11-23 03:38:45 +03:00
req - > WriteChannelInfoLength =
2018-01-25 08:07:41 +03:00
cpu_to_le16 ( sizeof ( struct smbd_buffer_descriptor_v1 ) ) ;
2017-11-23 03:38:45 +03:00
v1 = ( struct smbd_buffer_descriptor_v1 * ) & req - > Buffer [ 0 ] ;
2018-01-25 08:07:41 +03:00
v1 - > offset = cpu_to_le64 ( wdata - > mr - > mr - > iova ) ;
v1 - > token = cpu_to_le32 ( wdata - > mr - > mr - > rkey ) ;
v1 - > length = cpu_to_le32 ( wdata - > mr - > mr - > length ) ;
2017-11-23 03:38:45 +03:00
}
# endif
2018-06-12 01:00:58 +03:00
iov [ 0 ] . iov_len = total_len - 1 ;
iov [ 0 ] . iov_base = ( char * ) req ;
2012-09-19 03:20:29 +04:00
2016-11-24 02:14:57 +03:00
rqst . rq_iov = iov ;
2018-06-12 01:00:58 +03:00
rqst . rq_nvec = 1 ;
2012-09-19 03:20:35 +04:00
rqst . rq_pages = wdata - > pages ;
2018-05-30 22:47:53 +03:00
rqst . rq_offset = wdata - > page_offset ;
2012-09-19 03:20:35 +04:00
rqst . rq_npages = wdata - > nr_pages ;
rqst . rq_pagesz = wdata - > pagesz ;
rqst . rq_tailsz = wdata - > tailsz ;
2017-11-23 03:38:45 +03:00
# ifdef CONFIG_CIFS_SMB_DIRECT
if ( wdata - > mr ) {
2018-06-12 01:00:58 +03:00
iov [ 0 ] . iov_len + = sizeof ( struct smbd_buffer_descriptor_v1 ) ;
2017-11-23 03:38:45 +03:00
rqst . rq_npages = 0 ;
}
# endif
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " async write at %llu %u bytes \n " ,
wdata - > offset , wdata - > bytes ) ;
2012-09-19 03:20:29 +04:00
2017-11-23 03:38:45 +03:00
# ifdef CONFIG_CIFS_SMB_DIRECT
/* For RDMA read, I/O size is in RemainingBytes not in Length */
if ( ! wdata - > mr )
req - > Length = cpu_to_le32 ( wdata - > bytes ) ;
# else
2012-09-19 03:20:29 +04:00
req - > Length = cpu_to_le32 ( wdata - > bytes ) ;
2017-11-23 03:38:45 +03:00
# endif
2012-09-19 03:20:29 +04:00
2014-06-05 19:03:27 +04:00
if ( wdata - > credits ) {
2016-10-25 01:33:04 +03:00
shdr - > CreditCharge = cpu_to_le16 ( DIV_ROUND_UP ( wdata - > bytes ,
2014-06-05 19:03:27 +04:00
SMB2_MAX_BUFFER_SIZE ) ) ;
2018-12-20 01:49:09 +03:00
shdr - > CreditRequest =
cpu_to_le16 ( le16_to_cpu ( shdr - > CreditCharge ) + 1 ) ;
2014-06-05 19:03:27 +04:00
spin_lock ( & server - > req_lock ) ;
server - > credits + = wdata - > credits -
2016-10-25 01:33:04 +03:00
le16_to_cpu ( shdr - > CreditCharge ) ;
2014-06-05 19:03:27 +04:00
spin_unlock ( & server - > req_lock ) ;
wake_up ( & server - > request_q ) ;
2018-12-20 01:49:09 +03:00
wdata - > credits = le16_to_cpu ( shdr - > CreditCharge ) ;
2016-10-31 23:49:30 +03:00
flags | = CIFS_HAS_CREDITS ;
2014-06-05 19:03:27 +04:00
}
2012-09-19 03:20:29 +04:00
kref_get ( & wdata - > refcount ) ;
2016-11-17 01:06:17 +03:00
rc = cifs_call_async ( server , & rqst , NULL , smb2_writev_callback , NULL ,
wdata , flags ) ;
2012-09-19 03:20:29 +04:00
2012-09-19 16:03:26 +04:00
if ( rc ) {
2018-05-18 05:16:55 +03:00
trace_smb3_write_err ( 0 /* no xid */ , req - > PersistentFileId ,
tcon - > tid , tcon - > ses - > Suid , wdata - > offset ,
wdata - > bytes , rc ) ;
2014-02-08 06:45:12 +04:00
kref_put ( & wdata - > refcount , release ) ;
2012-09-19 16:03:26 +04:00
cifs_stats_fail_inc ( tcon , SMB2_WRITE_HE ) ;
2018-05-18 05:16:55 +03:00
} else
trace_smb3_write_done ( 0 /* no xid */ , req - > PersistentFileId ,
tcon - > tid , tcon - > ses - > Suid , wdata - > offset ,
wdata - > bytes ) ;
2012-09-19 03:20:29 +04:00
async_writev_out :
cifs_small_buf_release ( req ) ;
return rc ;
}
2012-09-19 03:20:30 +04:00
/*
* SMB2_write function gets iov pointer to kvec array with n_vec as a length .
* The length field from io_parms must be at least 1 and indicates a number of
* elements with data to write that begins with position 1 in iov array . All
* data length is specified by count .
*/
int
SMB2_write ( const unsigned int xid , struct cifs_io_parms * io_parms ,
unsigned int * nbytes , struct kvec * iov , int n_vec )
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2012-09-19 03:20:30 +04:00
int rc = 0 ;
struct smb2_write_req * req = NULL ;
struct smb2_write_rsp * rsp = NULL ;
int resp_buftype ;
2016-10-25 21:38:47 +03:00
struct kvec rsp_iov ;
2016-10-31 23:49:30 +03:00
int flags = 0 ;
2017-11-20 03:24:41 +03:00
unsigned int total_len ;
2016-10-25 21:38:47 +03:00
2012-09-19 03:20:30 +04:00
* nbytes = 0 ;
if ( n_vec < 1 )
return rc ;
2017-11-20 03:24:41 +03:00
rc = smb2_plain_req_init ( SMB2_WRITE , io_parms - > tcon , ( void * * ) & req ,
& total_len ) ;
2012-09-19 03:20:30 +04:00
if ( rc )
return rc ;
if ( io_parms - > tcon - > ses - > server = = NULL )
return - ECONNABORTED ;
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( io_parms - > tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2017-11-20 03:24:41 +03:00
req - > sync_hdr . ProcessId = cpu_to_le32 ( io_parms - > pid ) ;
2012-09-19 03:20:30 +04:00
req - > PersistentFileId = io_parms - > persistent_fid ;
req - > VolatileFileId = io_parms - > volatile_fid ;
req - > WriteChannelInfoOffset = 0 ;
req - > WriteChannelInfoLength = 0 ;
req - > Channel = 0 ;
req - > Length = cpu_to_le32 ( io_parms - > length ) ;
req - > Offset = cpu_to_le64 ( io_parms - > offset ) ;
req - > DataOffset = cpu_to_le16 (
2017-11-20 03:24:41 +03:00
offsetof ( struct smb2_write_req , Buffer ) ) ;
2012-09-19 03:20:30 +04:00
req - > RemainingBytes = 0 ;
iov [ 0 ] . iov_base = ( char * ) req ;
2017-11-20 03:24:41 +03:00
/* 1 for Buffer */
iov [ 0 ] . iov_len = total_len - 1 ;
2012-09-19 03:20:30 +04:00
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = n_vec + 1 ;
rc = cifs_send_recv ( xid , io_parms - > tcon - > ses , & rqst ,
2017-11-20 03:24:41 +03:00
& resp_buftype , flags , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( req ) ;
rsp = ( struct smb2_write_rsp * ) rsp_iov . iov_base ;
2012-09-19 03:20:30 +04:00
if ( rc ) {
2018-05-18 05:16:55 +03:00
trace_smb3_write_err ( xid , req - > PersistentFileId ,
io_parms - > tcon - > tid ,
io_parms - > tcon - > ses - > Suid ,
io_parms - > offset , io_parms - > length , rc ) ;
2012-09-19 03:20:30 +04:00
cifs_stats_fail_inc ( io_parms - > tcon , SMB2_WRITE_HE ) ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " Send error in write = %d \n " , rc ) ;
2018-05-18 05:16:55 +03:00
} else {
2012-09-19 03:20:30 +04:00
* nbytes = le32_to_cpu ( rsp - > DataLength ) ;
2018-05-18 05:16:55 +03:00
trace_smb3_write_done ( xid , req - > PersistentFileId ,
io_parms - > tcon - > tid ,
io_parms - > tcon - > ses - > Suid ,
io_parms - > offset , * nbytes ) ;
}
2012-09-19 16:03:26 +04:00
free_rsp_buf ( resp_buftype , rsp ) ;
2012-09-19 03:20:30 +04:00
return rc ;
}
2012-09-19 03:20:31 +04:00
2012-09-19 03:20:33 +04:00
static unsigned int
num_entries ( char * bufstart , char * end_of_buf , char * * lastentry , size_t size )
{
int len ;
unsigned int entrycount = 0 ;
unsigned int next_offset = 0 ;
2018-09-06 12:48:22 +03:00
char * entryptr ;
FILE_DIRECTORY_INFO * dir_info ;
2012-09-19 03:20:33 +04:00
if ( bufstart = = NULL )
return 0 ;
2018-09-06 12:48:22 +03:00
entryptr = bufstart ;
2012-09-19 03:20:33 +04:00
while ( 1 ) {
2018-09-06 12:48:22 +03:00
if ( entryptr + next_offset < entryptr | |
entryptr + next_offset > end_of_buf | |
entryptr + next_offset + size > end_of_buf ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " malformed search entry would overflow \n " ) ;
2012-09-19 03:20:33 +04:00
break ;
}
2018-09-06 12:48:22 +03:00
entryptr = entryptr + next_offset ;
dir_info = ( FILE_DIRECTORY_INFO * ) entryptr ;
len = le32_to_cpu ( dir_info - > FileNameLength ) ;
if ( entryptr + len < entryptr | |
entryptr + len > end_of_buf | |
entryptr + len + size > end_of_buf ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " directory entry name would overflow frame end of buf %p \n " ,
end_of_buf ) ;
2012-09-19 03:20:33 +04:00
break ;
}
2018-09-06 12:48:22 +03:00
* lastentry = entryptr ;
2012-09-19 03:20:33 +04:00
entrycount + + ;
2018-09-06 12:48:22 +03:00
next_offset = le32_to_cpu ( dir_info - > NextEntryOffset ) ;
2012-09-19 03:20:33 +04:00
if ( ! next_offset )
break ;
}
return entrycount ;
}
/*
* Readdir / FindFirst
*/
int
SMB2_query_directory ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , int index ,
struct cifs_search_info * srch_inf )
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2012-09-19 03:20:33 +04:00
struct smb2_query_directory_req * req ;
struct smb2_query_directory_rsp * rsp = NULL ;
struct kvec iov [ 2 ] ;
2016-10-25 21:38:47 +03:00
struct kvec rsp_iov ;
2012-09-19 03:20:33 +04:00
int rc = 0 ;
int len ;
2015-03-26 02:51:57 +03:00
int resp_buftype = CIFS_NO_BUFFER ;
2012-09-19 03:20:33 +04:00
unsigned char * bufptr ;
struct TCP_Server_Info * server ;
struct cifs_ses * ses = tcon - > ses ;
__le16 asteriks = cpu_to_le16 ( ' * ' ) ;
char * end_of_smb ;
unsigned int output_size = CIFSMaxBufSize ;
size_t info_buf_size ;
2016-10-31 23:49:30 +03:00
int flags = 0 ;
2017-11-20 03:24:45 +03:00
unsigned int total_len ;
2012-09-19 03:20:33 +04:00
if ( ses & & ( ses - > server ) )
server = ses - > server ;
else
return - EIO ;
2017-11-20 03:24:45 +03:00
rc = smb2_plain_req_init ( SMB2_QUERY_DIRECTORY , tcon , ( void * * ) & req ,
& total_len ) ;
2012-09-19 03:20:33 +04:00
if ( rc )
return rc ;
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2012-09-19 03:20:33 +04:00
switch ( srch_inf - > info_level ) {
case SMB_FIND_FILE_DIRECTORY_INFO :
req - > FileInformationClass = FILE_DIRECTORY_INFORMATION ;
info_buf_size = sizeof ( FILE_DIRECTORY_INFO ) - 1 ;
break ;
case SMB_FIND_FILE_ID_FULL_DIR_INFO :
req - > FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION ;
info_buf_size = sizeof ( SEARCH_ID_FULL_DIR_INFO ) - 1 ;
break ;
default :
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " info level %u isn't supported \n " ,
srch_inf - > info_level ) ;
2012-09-19 03:20:33 +04:00
rc = - EINVAL ;
goto qdir_exit ;
}
req - > FileIndex = cpu_to_le32 ( index ) ;
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
len = 0x2 ;
bufptr = req - > Buffer ;
memcpy ( bufptr , & asteriks , len ) ;
req - > FileNameOffset =
2017-11-20 03:24:45 +03:00
cpu_to_le16 ( sizeof ( struct smb2_query_directory_req ) - 1 ) ;
2012-09-19 03:20:33 +04:00
req - > FileNameLength = cpu_to_le16 ( len ) ;
/*
* BB could be 30 bytes or so longer if we used SMB2 specific
* buffer lengths , but this is safe and close enough .
*/
output_size = min_t ( unsigned int , output_size , server - > maxBuf ) ;
output_size = min_t ( unsigned int , output_size , 2 < < 15 ) ;
req - > OutputBufferLength = cpu_to_le32 ( output_size ) ;
iov [ 0 ] . iov_base = ( char * ) req ;
2017-11-20 03:24:45 +03:00
/* 1 for Buffer */
iov [ 0 ] . iov_len = total_len - 1 ;
2012-09-19 03:20:33 +04:00
iov [ 1 ] . iov_base = ( char * ) ( req - > Buffer ) ;
iov [ 1 ] . iov_len = len ;
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = 2 ;
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( req ) ;
rsp = ( struct smb2_query_directory_rsp * ) rsp_iov . iov_base ;
2012-09-19 16:03:26 +04:00
2012-09-19 03:20:33 +04:00
if ( rc ) {
2016-10-25 01:33:04 +03:00
if ( rc = = - ENODATA & &
2018-06-01 03:53:06 +03:00
rsp - > sync_hdr . Status = = STATUS_NO_MORE_FILES ) {
2014-08-18 20:49:57 +04:00
srch_inf - > endOfSearch = true ;
rc = 0 ;
}
2012-09-19 03:20:33 +04:00
cifs_stats_fail_inc ( tcon , SMB2_QUERY_DIRECTORY_HE ) ;
goto qdir_exit ;
}
2018-08-08 08:07:49 +03:00
rc = smb2_validate_iov ( le16_to_cpu ( rsp - > OutputBufferOffset ) ,
le32_to_cpu ( rsp - > OutputBufferLength ) , & rsp_iov ,
info_buf_size ) ;
2012-09-19 03:20:33 +04:00
if ( rc )
goto qdir_exit ;
srch_inf - > unicode = true ;
if ( srch_inf - > ntwrk_buf_start ) {
if ( srch_inf - > smallBuf )
cifs_small_buf_release ( srch_inf - > ntwrk_buf_start ) ;
else
cifs_buf_release ( srch_inf - > ntwrk_buf_start ) ;
}
srch_inf - > ntwrk_buf_start = ( char * ) rsp ;
2018-06-01 03:53:02 +03:00
srch_inf - > srch_entries_start = srch_inf - > last_entry =
( char * ) rsp + le16_to_cpu ( rsp - > OutputBufferOffset ) ;
end_of_smb = rsp_iov . iov_len + ( char * ) rsp ;
2012-09-19 03:20:33 +04:00
srch_inf - > entries_in_buffer =
num_entries ( srch_inf - > srch_entries_start , end_of_smb ,
& srch_inf - > last_entry , info_buf_size ) ;
srch_inf - > index_of_last_entry + = srch_inf - > entries_in_buffer ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " num entries %d last_index %lld srch start %p srch end %p \n " ,
srch_inf - > entries_in_buffer , srch_inf - > index_of_last_entry ,
srch_inf - > srch_entries_start , srch_inf - > last_entry ) ;
2012-09-19 03:20:33 +04:00
if ( resp_buftype = = CIFS_LARGE_BUFFER )
srch_inf - > smallBuf = false ;
else if ( resp_buftype = = CIFS_SMALL_BUFFER )
srch_inf - > smallBuf = true ;
else
2013-05-05 07:12:25 +04:00
cifs_dbg ( VFS , " illegal search buffer type \n " ) ;
2012-09-19 03:20:33 +04:00
return rc ;
qdir_exit :
free_rsp_buf ( resp_buftype , rsp ) ;
return rc ;
}
2018-09-03 06:33:44 +03:00
int
SMB2_set_info_init ( struct cifs_tcon * tcon , struct smb_rqst * rqst ,
2017-06-29 06:37:00 +03:00
u64 persistent_fid , u64 volatile_fid , u32 pid , u8 info_class ,
2018-09-03 06:33:44 +03:00
u8 info_type , u32 additional_info ,
2017-06-29 06:37:00 +03:00
void * * data , unsigned int * size )
2012-09-19 03:20:31 +04:00
{
struct smb2_set_info_req * req ;
2018-09-03 06:33:44 +03:00
struct kvec * iov = rqst - > rq_iov ;
unsigned int i , total_len ;
int rc ;
2012-09-19 03:20:31 +04:00
2017-11-20 03:24:44 +03:00
rc = smb2_plain_req_init ( SMB2_SET_INFO , tcon , ( void * * ) & req , & total_len ) ;
2018-09-03 06:33:44 +03:00
if ( rc )
2012-09-19 03:20:31 +04:00
return rc ;
2016-10-31 23:49:30 +03:00
2017-11-20 03:24:44 +03:00
req - > sync_hdr . ProcessId = cpu_to_le32 ( pid ) ;
2017-06-29 06:37:00 +03:00
req - > InfoType = info_type ;
2012-09-19 03:20:31 +04:00
req - > FileInfoClass = info_class ;
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
2017-06-29 06:37:00 +03:00
req - > AdditionalInformation = cpu_to_le32 ( additional_info ) ;
2012-09-19 03:20:31 +04:00
req - > BufferOffset =
2017-11-20 03:24:44 +03:00
cpu_to_le16 ( sizeof ( struct smb2_set_info_req ) - 1 ) ;
2012-09-19 03:20:31 +04:00
req - > BufferLength = cpu_to_le32 ( * size ) ;
memcpy ( req - > Buffer , * data , * size ) ;
2017-11-20 03:24:44 +03:00
total_len + = * size ;
2012-09-19 03:20:31 +04:00
iov [ 0 ] . iov_base = ( char * ) req ;
2017-11-20 03:24:44 +03:00
/* 1 for Buffer */
iov [ 0 ] . iov_len = total_len - 1 ;
2012-09-19 03:20:31 +04:00
2018-09-03 06:33:44 +03:00
for ( i = 1 ; i < rqst - > rq_nvec ; i + + ) {
2012-09-19 03:20:31 +04:00
le32_add_cpu ( & req - > BufferLength , size [ i ] ) ;
iov [ i ] . iov_base = ( char * ) data [ i ] ;
iov [ i ] . iov_len = size [ i ] ;
}
2018-09-03 06:33:44 +03:00
return 0 ;
}
void
SMB2_set_info_free ( struct smb_rqst * rqst )
{
2018-10-24 04:50:33 +03:00
if ( rqst & & rqst - > rq_iov )
cifs_buf_release ( rqst - > rq_iov [ 0 ] . iov_base ) ; /* request */
2018-09-03 06:33:44 +03:00
}
static int
send_set_info ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , u32 pid , u8 info_class ,
u8 info_type , u32 additional_info , unsigned int num ,
void * * data , unsigned int * size )
{
struct smb_rqst rqst ;
struct smb2_set_info_rsp * rsp = NULL ;
struct kvec * iov ;
struct kvec rsp_iov ;
int rc = 0 ;
int resp_buftype ;
struct cifs_ses * ses = tcon - > ses ;
int flags = 0 ;
if ( ! ses | | ! ( ses - > server ) )
return - EIO ;
if ( ! num )
return - EINVAL ;
if ( smb3_encryption_required ( tcon ) )
flags | = CIFS_TRANSFORM_REQ ;
iov = kmalloc_array ( num , sizeof ( struct kvec ) , GFP_KERNEL ) ;
if ( ! iov )
return - ENOMEM ;
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = num ;
2018-09-03 06:33:44 +03:00
rc = SMB2_set_info_init ( tcon , & rqst , persistent_fid , volatile_fid , pid ,
info_class , info_type , additional_info ,
data , size ) ;
if ( rc ) {
kfree ( iov ) ;
return rc ;
}
2018-06-12 01:00:59 +03:00
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags ,
2017-11-20 03:24:44 +03:00
& rsp_iov ) ;
2018-09-03 06:33:44 +03:00
SMB2_set_info_free ( & rqst ) ;
2016-10-25 21:38:47 +03:00
rsp = ( struct smb2_set_info_rsp * ) rsp_iov . iov_base ;
2012-09-19 03:20:31 +04:00
2018-05-18 05:16:55 +03:00
if ( rc ! = 0 ) {
2012-09-19 03:20:31 +04:00
cifs_stats_fail_inc ( tcon , SMB2_SET_INFO_HE ) ;
2018-05-18 05:16:55 +03:00
trace_smb3_set_info_err ( xid , persistent_fid , tcon - > tid ,
ses - > Suid , info_class , ( __u32 ) info_type , rc ) ;
}
2013-11-18 19:56:28 +04:00
2012-09-19 03:20:31 +04:00
free_rsp_buf ( resp_buftype , rsp ) ;
kfree ( iov ) ;
return rc ;
}
2012-09-19 03:20:32 +04:00
int
SMB2_set_eof ( const unsigned int xid , struct cifs_tcon * tcon , u64 persistent_fid ,
2018-09-03 06:33:47 +03:00
u64 volatile_fid , u32 pid , __le64 * eof )
2012-09-19 03:20:32 +04:00
{
struct smb2_file_eof_info info ;
void * data ;
unsigned int size ;
info . EndOfFile = * eof ;
data = & info ;
size = sizeof ( struct smb2_file_eof_info ) ;
2018-09-03 06:33:47 +03:00
return send_set_info ( xid , tcon , persistent_fid , volatile_fid ,
2017-06-29 06:37:00 +03:00
pid , FILE_END_OF_FILE_INFORMATION , SMB2_O_INFO_FILE ,
0 , 1 , & data , & size ) ;
2012-09-19 03:20:32 +04:00
}
2012-09-19 03:20:32 +04:00
2017-06-29 06:37:00 +03:00
int
SMB2_set_acl ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid ,
struct cifs_ntsd * pnntsd , int pacllen , int aclflag )
{
return send_set_info ( xid , tcon , persistent_fid , volatile_fid ,
current - > tgid , 0 , SMB2_O_INFO_SECURITY , aclflag ,
1 , ( void * * ) & pnntsd , & pacllen ) ;
2012-09-19 03:20:32 +04:00
}
2012-09-19 03:20:33 +04:00
2017-08-24 04:24:56 +03:00
int
SMB2_set_ea ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid ,
struct smb2_file_full_ea_info * buf , int len )
{
return send_set_info ( xid , tcon , persistent_fid , volatile_fid ,
current - > tgid , FILE_FULL_EA_INFORMATION , SMB2_O_INFO_FILE ,
0 , 1 , ( void * * ) & buf , & len ) ;
}
2012-09-19 03:20:33 +04:00
int
SMB2_oplock_break ( const unsigned int xid , struct cifs_tcon * tcon ,
const u64 persistent_fid , const u64 volatile_fid ,
__u8 oplock_level )
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2012-09-19 03:20:33 +04:00
int rc ;
2018-06-01 03:53:03 +03:00
struct smb2_oplock_break * req = NULL ;
2017-11-20 03:24:43 +03:00
struct cifs_ses * ses = tcon - > ses ;
2016-10-31 23:49:30 +03:00
int flags = CIFS_OBREAK_OP ;
2017-11-20 03:24:43 +03:00
unsigned int total_len ;
struct kvec iov [ 1 ] ;
struct kvec rsp_iov ;
int resp_buf_type ;
2012-09-19 03:20:33 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " SMB2_oplock_break \n " ) ;
2017-11-20 03:24:43 +03:00
rc = smb2_plain_req_init ( SMB2_OPLOCK_BREAK , tcon , ( void * * ) & req ,
& total_len ) ;
2012-09-19 03:20:33 +04:00
if ( rc )
return rc ;
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2012-09-19 03:20:33 +04:00
req - > VolatileFid = volatile_fid ;
req - > PersistentFid = persistent_fid ;
req - > OplockLevel = oplock_level ;
2017-11-20 03:24:43 +03:00
req - > sync_hdr . CreditRequest = cpu_to_le16 ( 1 ) ;
2012-09-19 03:20:33 +04:00
2017-11-20 03:24:43 +03:00
flags | = CIFS_NO_RESP ;
iov [ 0 ] . iov_base = ( char * ) req ;
iov [ 0 ] . iov_len = total_len ;
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = 1 ;
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buf_type , flags , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( req ) ;
2012-09-19 03:20:33 +04:00
if ( rc ) {
cifs_stats_fail_inc ( tcon , SMB2_OPLOCK_BREAK_HE ) ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Send error in Oplock Break = %d \n " , rc ) ;
2012-09-19 03:20:33 +04:00
}
return rc ;
}
2012-09-19 03:20:34 +04:00
2018-08-08 08:07:49 +03:00
void
smb2_copy_fs_info_to_kstatfs ( struct smb2_fs_full_size_info * pfs_inf ,
struct kstatfs * kst )
2012-09-19 03:20:34 +04:00
{
kst - > f_bsize = le32_to_cpu ( pfs_inf - > BytesPerSector ) *
le32_to_cpu ( pfs_inf - > SectorsPerAllocationUnit ) ;
kst - > f_blocks = le64_to_cpu ( pfs_inf - > TotalAllocationUnits ) ;
2017-08-03 10:39:03 +03:00
kst - > f_bfree = kst - > f_bavail =
le64_to_cpu ( pfs_inf - > CallerAvailableAllocationUnits ) ;
2012-09-19 03:20:34 +04:00
return ;
}
2018-06-25 07:28:12 +03:00
static void
copy_posix_fs_info_to_kstatfs ( FILE_SYSTEM_POSIX_INFO * response_data ,
struct kstatfs * kst )
{
kst - > f_bsize = le32_to_cpu ( response_data - > BlockSize ) ;
kst - > f_blocks = le64_to_cpu ( response_data - > TotalBlocks ) ;
kst - > f_bfree = le64_to_cpu ( response_data - > BlocksAvail ) ;
if ( response_data - > UserBlocksAvail = = cpu_to_le64 ( - 1 ) )
kst - > f_bavail = kst - > f_bfree ;
else
kst - > f_bavail = le64_to_cpu ( response_data - > UserBlocksAvail ) ;
if ( response_data - > TotalFileNodes ! = cpu_to_le64 ( - 1 ) )
kst - > f_files = le64_to_cpu ( response_data - > TotalFileNodes ) ;
if ( response_data - > FreeFileNodes ! = cpu_to_le64 ( - 1 ) )
kst - > f_ffree = le64_to_cpu ( response_data - > FreeFileNodes ) ;
return ;
}
2012-09-19 03:20:34 +04:00
static int
build_qfs_info_req ( struct kvec * iov , struct cifs_tcon * tcon , int level ,
int outbuf_len , u64 persistent_fid , u64 volatile_fid )
{
int rc ;
struct smb2_query_info_req * req ;
2017-11-20 03:24:46 +03:00
unsigned int total_len ;
2012-09-19 03:20:34 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Query FSInfo level %d \n " , level ) ;
2012-09-19 03:20:34 +04:00
if ( ( tcon - > ses = = NULL ) | | ( tcon - > ses - > server = = NULL ) )
return - EIO ;
2017-11-20 03:24:46 +03:00
rc = smb2_plain_req_init ( SMB2_QUERY_INFO , tcon , ( void * * ) & req ,
& total_len ) ;
2012-09-19 03:20:34 +04:00
if ( rc )
return rc ;
req - > InfoType = SMB2_O_INFO_FILESYSTEM ;
req - > FileInfoClass = level ;
req - > PersistentFileId = persistent_fid ;
req - > VolatileFileId = volatile_fid ;
2017-11-20 03:24:46 +03:00
/* 1 for pad */
2012-09-19 03:20:34 +04:00
req - > InputBufferOffset =
2017-11-20 03:24:46 +03:00
cpu_to_le16 ( sizeof ( struct smb2_query_info_req ) - 1 ) ;
2012-09-19 03:20:34 +04:00
req - > OutputBufferLength = cpu_to_le32 (
2018-06-01 03:53:07 +03:00
outbuf_len + sizeof ( struct smb2_query_info_rsp ) - 1 ) ;
2012-09-19 03:20:34 +04:00
iov - > iov_base = ( char * ) req ;
2017-11-20 03:24:46 +03:00
iov - > iov_len = total_len ;
2012-09-19 03:20:34 +04:00
return 0 ;
}
2018-06-25 07:28:12 +03:00
int
SMB311_posix_qfs_info ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , struct kstatfs * fsdata )
{
struct smb_rqst rqst ;
struct smb2_query_info_rsp * rsp = NULL ;
struct kvec iov ;
struct kvec rsp_iov ;
int rc = 0 ;
int resp_buftype ;
struct cifs_ses * ses = tcon - > ses ;
FILE_SYSTEM_POSIX_INFO * info = NULL ;
int flags = 0 ;
rc = build_qfs_info_req ( & iov , tcon , FS_POSIX_INFORMATION ,
sizeof ( FILE_SYSTEM_POSIX_INFO ) ,
persistent_fid , volatile_fid ) ;
if ( rc )
return rc ;
if ( smb3_encryption_required ( tcon ) )
flags | = CIFS_TRANSFORM_REQ ;
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = & iov ;
rqst . rq_nvec = 1 ;
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags , & rsp_iov ) ;
cifs_small_buf_release ( iov . iov_base ) ;
if ( rc ) {
cifs_stats_fail_inc ( tcon , SMB2_QUERY_INFO_HE ) ;
goto posix_qfsinf_exit ;
}
rsp = ( struct smb2_query_info_rsp * ) rsp_iov . iov_base ;
info = ( FILE_SYSTEM_POSIX_INFO * ) (
le16_to_cpu ( rsp - > OutputBufferOffset ) + ( char * ) rsp ) ;
2018-08-08 08:07:49 +03:00
rc = smb2_validate_iov ( le16_to_cpu ( rsp - > OutputBufferOffset ) ,
le32_to_cpu ( rsp - > OutputBufferLength ) , & rsp_iov ,
sizeof ( FILE_SYSTEM_POSIX_INFO ) ) ;
2018-06-25 07:28:12 +03:00
if ( ! rc )
copy_posix_fs_info_to_kstatfs ( info , fsdata ) ;
posix_qfsinf_exit :
free_rsp_buf ( resp_buftype , rsp_iov . iov_base ) ;
return rc ;
}
2012-09-19 03:20:34 +04:00
int
SMB2_QFS_info ( const unsigned int xid , struct cifs_tcon * tcon ,
u64 persistent_fid , u64 volatile_fid , struct kstatfs * fsdata )
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2012-09-19 03:20:34 +04:00
struct smb2_query_info_rsp * rsp = NULL ;
struct kvec iov ;
2016-10-25 21:38:47 +03:00
struct kvec rsp_iov ;
2012-09-19 03:20:34 +04:00
int rc = 0 ;
int resp_buftype ;
struct cifs_ses * ses = tcon - > ses ;
struct smb2_fs_full_size_info * info = NULL ;
2016-10-31 23:49:30 +03:00
int flags = 0 ;
2012-09-19 03:20:34 +04:00
rc = build_qfs_info_req ( & iov , tcon , FS_FULL_SIZE_INFORMATION ,
sizeof ( struct smb2_fs_full_size_info ) ,
persistent_fid , volatile_fid ) ;
if ( rc )
return rc ;
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = & iov ;
rqst . rq_nvec = 1 ;
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( iov . iov_base ) ;
2012-09-19 03:20:34 +04:00
if ( rc ) {
cifs_stats_fail_inc ( tcon , SMB2_QUERY_INFO_HE ) ;
2013-10-09 11:07:00 +04:00
goto qfsinf_exit ;
2012-09-19 03:20:34 +04:00
}
2016-10-25 21:38:47 +03:00
rsp = ( struct smb2_query_info_rsp * ) rsp_iov . iov_base ;
2012-09-19 03:20:34 +04:00
2018-06-01 03:53:07 +03:00
info = ( struct smb2_fs_full_size_info * ) (
2018-06-01 03:53:06 +03:00
le16_to_cpu ( rsp - > OutputBufferOffset ) + ( char * ) rsp ) ;
2018-08-08 08:07:49 +03:00
rc = smb2_validate_iov ( le16_to_cpu ( rsp - > OutputBufferOffset ) ,
le32_to_cpu ( rsp - > OutputBufferLength ) , & rsp_iov ,
sizeof ( struct smb2_fs_full_size_info ) ) ;
2012-09-19 03:20:34 +04:00
if ( ! rc )
2018-08-08 08:07:49 +03:00
smb2_copy_fs_info_to_kstatfs ( info , fsdata ) ;
2012-09-19 03:20:34 +04:00
2013-10-09 11:07:00 +04:00
qfsinf_exit :
2016-10-25 21:38:47 +03:00
free_rsp_buf ( resp_buftype , rsp_iov . iov_base ) ;
2013-10-09 11:07:00 +04:00
return rc ;
}
int
SMB2_QFS_attr ( const unsigned int xid , struct cifs_tcon * tcon ,
2013-10-09 22:36:35 +04:00
u64 persistent_fid , u64 volatile_fid , int level )
2013-10-09 11:07:00 +04:00
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2013-10-09 11:07:00 +04:00
struct smb2_query_info_rsp * rsp = NULL ;
struct kvec iov ;
2016-10-25 21:38:47 +03:00
struct kvec rsp_iov ;
2013-10-09 11:07:00 +04:00
int rc = 0 ;
2013-10-09 22:36:35 +04:00
int resp_buftype , max_len , min_len ;
2013-10-09 11:07:00 +04:00
struct cifs_ses * ses = tcon - > ses ;
unsigned int rsp_len , offset ;
2016-10-31 23:49:30 +03:00
int flags = 0 ;
2013-10-09 11:07:00 +04:00
2013-10-09 22:36:35 +04:00
if ( level = = FS_DEVICE_INFORMATION ) {
max_len = sizeof ( FILE_SYSTEM_DEVICE_INFO ) ;
min_len = sizeof ( FILE_SYSTEM_DEVICE_INFO ) ;
} else if ( level = = FS_ATTRIBUTE_INFORMATION ) {
max_len = sizeof ( FILE_SYSTEM_ATTRIBUTE_INFO ) ;
min_len = MIN_FS_ATTR_INFO_SIZE ;
2013-10-10 05:55:53 +04:00
} else if ( level = = FS_SECTOR_SIZE_INFORMATION ) {
max_len = sizeof ( struct smb3_fs_ss_info ) ;
min_len = sizeof ( struct smb3_fs_ss_info ) ;
2018-06-25 07:18:52 +03:00
} else if ( level = = FS_VOLUME_INFORMATION ) {
max_len = sizeof ( struct smb3_fs_vol_info ) + MAX_VOL_LABEL_LEN ;
min_len = sizeof ( struct smb3_fs_vol_info ) ;
2013-10-09 22:36:35 +04:00
} else {
2013-10-10 05:55:53 +04:00
cifs_dbg ( FYI , " Invalid qfsinfo level %d \n " , level ) ;
2013-10-09 22:36:35 +04:00
return - EINVAL ;
}
rc = build_qfs_info_req ( & iov , tcon , level , max_len ,
2013-10-09 11:07:00 +04:00
persistent_fid , volatile_fid ) ;
if ( rc )
return rc ;
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = & iov ;
rqst . rq_nvec = 1 ;
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buftype , flags , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( iov . iov_base ) ;
2013-10-09 11:07:00 +04:00
if ( rc ) {
cifs_stats_fail_inc ( tcon , SMB2_QUERY_INFO_HE ) ;
goto qfsattr_exit ;
}
2016-10-25 21:38:47 +03:00
rsp = ( struct smb2_query_info_rsp * ) rsp_iov . iov_base ;
2013-10-09 11:07:00 +04:00
rsp_len = le32_to_cpu ( rsp - > OutputBufferLength ) ;
offset = le16_to_cpu ( rsp - > OutputBufferOffset ) ;
2018-08-08 08:07:49 +03:00
rc = smb2_validate_iov ( offset , rsp_len , & rsp_iov , min_len ) ;
2013-10-09 22:36:35 +04:00
if ( rc )
goto qfsattr_exit ;
if ( level = = FS_ATTRIBUTE_INFORMATION )
2018-06-01 03:53:07 +03:00
memcpy ( & tcon - > fsAttrInfo , offset
2018-06-01 03:53:06 +03:00
+ ( char * ) rsp , min_t ( unsigned int ,
2013-10-09 22:36:35 +04:00
rsp_len , max_len ) ) ;
else if ( level = = FS_DEVICE_INFORMATION )
2018-06-01 03:53:07 +03:00
memcpy ( & tcon - > fsDevInfo , offset
2018-06-01 03:53:06 +03:00
+ ( char * ) rsp , sizeof ( FILE_SYSTEM_DEVICE_INFO ) ) ;
2013-10-10 05:55:53 +04:00
else if ( level = = FS_SECTOR_SIZE_INFORMATION ) {
struct smb3_fs_ss_info * ss_info = ( struct smb3_fs_ss_info * )
2018-06-01 03:53:07 +03:00
( offset + ( char * ) rsp ) ;
2013-10-10 05:55:53 +04:00
tcon - > ss_flags = le32_to_cpu ( ss_info - > Flags ) ;
tcon - > perf_sector_size =
le32_to_cpu ( ss_info - > PhysicalBytesPerSectorForPerf ) ;
2018-06-25 07:18:52 +03:00
} else if ( level = = FS_VOLUME_INFORMATION ) {
struct smb3_fs_vol_info * vol_info = ( struct smb3_fs_vol_info * )
( offset + ( char * ) rsp ) ;
tcon - > vol_serial_number = vol_info - > VolumeSerialNumber ;
tcon - > vol_create_time = vol_info - > VolumeCreationTime ;
2013-10-10 05:55:53 +04:00
}
2013-10-09 11:07:00 +04:00
qfsattr_exit :
2016-10-25 21:38:47 +03:00
free_rsp_buf ( resp_buftype , rsp_iov . iov_base ) ;
2012-09-19 03:20:34 +04:00
return rc ;
}
2012-09-19 17:22:43 +04:00
int
smb2_lockv ( const unsigned int xid , struct cifs_tcon * tcon ,
const __u64 persist_fid , const __u64 volatile_fid , const __u32 pid ,
const __u32 num_lock , struct smb2_lock_element * buf )
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2012-09-19 17:22:43 +04:00
int rc = 0 ;
struct smb2_lock_req * req = NULL ;
struct kvec iov [ 2 ] ;
2016-10-25 21:38:47 +03:00
struct kvec rsp_iov ;
2012-09-19 17:22:43 +04:00
int resp_buf_type ;
unsigned int count ;
2016-10-31 23:49:30 +03:00
int flags = CIFS_NO_RESP ;
2017-11-21 02:07:27 +03:00
unsigned int total_len ;
2012-09-19 17:22:43 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " smb2_lockv num lock %d \n " , num_lock ) ;
2012-09-19 17:22:43 +04:00
2017-11-21 02:07:27 +03:00
rc = smb2_plain_req_init ( SMB2_LOCK , tcon , ( void * * ) & req , & total_len ) ;
2012-09-19 17:22:43 +04:00
if ( rc )
return rc ;
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2017-11-21 02:07:27 +03:00
req - > sync_hdr . ProcessId = cpu_to_le32 ( pid ) ;
2012-09-19 17:22:43 +04:00
req - > LockCount = cpu_to_le16 ( num_lock ) ;
req - > PersistentFileId = persist_fid ;
req - > VolatileFileId = volatile_fid ;
count = num_lock * sizeof ( struct smb2_lock_element ) ;
iov [ 0 ] . iov_base = ( char * ) req ;
2017-11-21 02:07:27 +03:00
iov [ 0 ] . iov_len = total_len - sizeof ( struct smb2_lock_element ) ;
2012-09-19 17:22:43 +04:00
iov [ 1 ] . iov_base = ( char * ) buf ;
iov [ 1 ] . iov_len = count ;
cifs_stats_inc ( & tcon - > stats . cifs_stats . num_locks ) ;
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = 2 ;
rc = cifs_send_recv ( xid , tcon - > ses , & rqst , & resp_buf_type , flags ,
2017-11-21 02:07:27 +03:00
& rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( req ) ;
2012-09-19 17:22:43 +04:00
if ( rc ) {
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Send error in smb2_lockv = %d \n " , rc ) ;
2012-09-19 17:22:43 +04:00
cifs_stats_fail_inc ( tcon , SMB2_LOCK_HE ) ;
2018-05-18 05:16:55 +03:00
trace_smb3_lock_err ( xid , persist_fid , tcon - > tid ,
tcon - > ses - > Suid , rc ) ;
2012-09-19 17:22:43 +04:00
}
return rc ;
}
int
SMB2_lock ( const unsigned int xid , struct cifs_tcon * tcon ,
const __u64 persist_fid , const __u64 volatile_fid , const __u32 pid ,
const __u64 length , const __u64 offset , const __u32 lock_flags ,
const bool wait )
{
struct smb2_lock_element lock ;
lock . Offset = cpu_to_le64 ( offset ) ;
lock . Length = cpu_to_le64 ( length ) ;
lock . Flags = cpu_to_le32 ( lock_flags ) ;
if ( ! wait & & lock_flags ! = SMB2_LOCKFLAG_UNLOCK )
lock . Flags | = cpu_to_le32 ( SMB2_LOCKFLAG_FAIL_IMMEDIATELY ) ;
return smb2_lockv ( xid , tcon , persist_fid , volatile_fid , pid , 1 , & lock ) ;
}
2012-09-19 17:22:45 +04:00
int
SMB2_lease_break ( const unsigned int xid , struct cifs_tcon * tcon ,
__u8 * lease_key , const __le32 lease_state )
{
2018-06-12 01:00:59 +03:00
struct smb_rqst rqst ;
2012-09-19 17:22:45 +04:00
int rc ;
struct smb2_lease_ack * req = NULL ;
2017-11-21 03:04:37 +03:00
struct cifs_ses * ses = tcon - > ses ;
2016-10-31 23:49:30 +03:00
int flags = CIFS_OBREAK_OP ;
2017-11-21 03:04:37 +03:00
unsigned int total_len ;
struct kvec iov [ 1 ] ;
struct kvec rsp_iov ;
int resp_buf_type ;
2018-09-29 03:44:23 +03:00
__u64 * please_key_high ;
__u64 * please_key_low ;
2012-09-19 17:22:45 +04:00
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " SMB2_lease_break \n " ) ;
2017-11-21 03:04:37 +03:00
rc = smb2_plain_req_init ( SMB2_OPLOCK_BREAK , tcon , ( void * * ) & req ,
& total_len ) ;
2012-09-19 17:22:45 +04:00
if ( rc )
return rc ;
2018-05-10 01:43:08 +03:00
if ( smb3_encryption_required ( tcon ) )
2016-10-31 23:49:30 +03:00
flags | = CIFS_TRANSFORM_REQ ;
2017-11-21 03:04:37 +03:00
req - > sync_hdr . CreditRequest = cpu_to_le16 ( 1 ) ;
2012-09-19 17:22:45 +04:00
req - > StructureSize = cpu_to_le16 ( 36 ) ;
2017-11-21 03:04:37 +03:00
total_len + = 12 ;
2012-09-19 17:22:45 +04:00
memcpy ( req - > LeaseKey , lease_key , 16 ) ;
req - > LeaseState = lease_state ;
2017-11-21 03:04:37 +03:00
flags | = CIFS_NO_RESP ;
iov [ 0 ] . iov_base = ( char * ) req ;
iov [ 0 ] . iov_len = total_len ;
2018-06-12 01:00:59 +03:00
memset ( & rqst , 0 , sizeof ( struct smb_rqst ) ) ;
rqst . rq_iov = iov ;
rqst . rq_nvec = 1 ;
rc = cifs_send_recv ( xid , ses , & rqst , & resp_buf_type , flags , & rsp_iov ) ;
2016-10-25 21:38:47 +03:00
cifs_small_buf_release ( req ) ;
2012-09-19 17:22:45 +04:00
2018-09-29 03:44:23 +03:00
please_key_low = ( __u64 * ) req - > LeaseKey ;
please_key_high = ( __u64 * ) ( req - > LeaseKey + 8 ) ;
2012-09-19 17:22:45 +04:00
if ( rc ) {
cifs_stats_fail_inc ( tcon , SMB2_OPLOCK_BREAK_HE ) ;
2018-09-29 03:44:23 +03:00
trace_smb3_lease_err ( le32_to_cpu ( lease_state ) , tcon - > tid ,
ses - > Suid , * please_key_low , * please_key_high , rc ) ;
2013-05-05 07:12:25 +04:00
cifs_dbg ( FYI , " Send error in Lease Break = %d \n " , rc ) ;
2018-09-29 03:44:23 +03:00
} else
trace_smb3_lease_done ( le32_to_cpu ( lease_state ) , tcon - > tid ,
ses - > Suid , * please_key_low , * please_key_high ) ;
2012-09-19 17:22:45 +04:00
return rc ;
}