mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 21:24:08 +08:00
13 cifs/smb3 fixes, mostly multichannel, reconnect relate
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmHk2A0ACgkQiiy9cAdy T1GjmgwAhJA5Z8nr28Q/DrKdUkdKeHbPQUsroQlRVzO7gswnVQe2EKc/cfBj2bC2 a/uD8G3Kkgv3n6UCjbj5FkfAQRIcuQaZfxPEYMrlk6quBmSeqSTts/YOGICfCvw8 4Ra/pULtuF3Y27/3z98owbMxi/OScVP7vOxGVSQOgBJazTX0Lgtnt+UK2gKMYqjD rGDaFScf8eq6J4Py2E2Ritn06v4Zk3/G1C+66SwJePnHdPfNh9ym+OKjiz8OkhB2 o968pt9QpBb6hizZlr/uO402lcxQkDVGmUKjHNA8xeCZvOvlBAlJFJY7FdfswlRv 62nxWhXg8hZBda1qJQS+7PLLK/2ovEFApcYk3ZZu67lCPHtk0cloxGkNmaJ7qcu1 /IDKFwPEiN3PqwE50hIHx9X3VaSdoOrL41MJVc1EzubM6kmgQHsq6FczKC8zWStH ZioulKkc6r+/bJWCkllMIFoo7hb/M67i8QLF9VSaepgxz1A1oN23N78X6rShzcjE 56qh83F+ =Kauh -----END PGP SIGNATURE----- Merge tag '5.17-rc-part1-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6 Pull cifs updates from Steve French: - multichannel patches mostly related to improving reconnect behavior - minor cleanup patches * tag '5.17-rc-part1-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6: cifs: fix FILE_BOTH_DIRECTORY_INFO definition cifs: move superblock magic defitions to magic.h cifs: Fix smb311_update_preauth_hash() kernel-doc comment cifs: avoid race during socket reconnect between send and recv cifs: maintain a state machine for tcp/smb/tcon sessions cifs: fix hang on cifs_get_next_mid() cifs: take cifs_tcp_ses_lock for status checks cifs: reconnect only the connection and not smb session where possible cifs: add WARN_ON for when chan_count goes below minimum cifs: adjust DebugData to use chans_need_reconnect for conn status cifs: use the chans_need_reconnect bitmap for reconnect status cifs: track individual channel status using chans_need_reconnect cifs: remove redundant assignment to pointer p
This commit is contained in:
commit
0c947b893d
@ -416,11 +416,17 @@ skip_rdma:
|
||||
from_kuid(&init_user_ns, ses->cred_uid));
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (CIFS_CHAN_NEEDS_RECONNECT(ses, 0))
|
||||
seq_puts(m, "\tPrimary channel: DISCONNECTED ");
|
||||
|
||||
if (ses->chan_count > 1) {
|
||||
seq_printf(m, "\n\n\tExtra Channels: %zu ",
|
||||
ses->chan_count-1);
|
||||
for (j = 1; j < ses->chan_count; j++)
|
||||
for (j = 1; j < ses->chan_count; j++) {
|
||||
cifs_dump_channel(m, j, &ses->chans[j]);
|
||||
if (CIFS_CHAN_NEEDS_RECONNECT(ses, j))
|
||||
seq_puts(m, "\tDISCONNECTED ");
|
||||
}
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
|
@ -84,9 +84,9 @@ struct key_type cifs_spnego_key_type = {
|
||||
|
||||
/* get a key struct with a SPNEGO security blob, suitable for session setup */
|
||||
struct key *
|
||||
cifs_get_spnego_key(struct cifs_ses *sesInfo)
|
||||
cifs_get_spnego_key(struct cifs_ses *sesInfo,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
struct TCP_Server_Info *server = cifs_ses_server(sesInfo);
|
||||
struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;
|
||||
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
|
||||
char *description, *dp;
|
||||
|
@ -29,7 +29,8 @@ struct cifs_spnego_msg {
|
||||
|
||||
#ifdef __KERNEL__
|
||||
extern struct key_type cifs_spnego_key_type;
|
||||
extern struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo);
|
||||
extern struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo,
|
||||
struct TCP_Server_Info *server);
|
||||
#endif /* KERNEL */
|
||||
|
||||
#endif /* _CIFS_SPNEGO_H */
|
||||
|
@ -498,10 +498,10 @@ static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *a
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (tcon->ses->server->tcpStatus != CifsExiting)
|
||||
tcon->ses->server->tcpStatus = CifsNeedReconnect;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&tcon->ses->server->srv_mutex);
|
||||
|
@ -141,9 +141,13 @@ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
|
||||
if ((cifs_pdu == NULL) || (server == NULL))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) ||
|
||||
server->tcpStatus == CifsNeedNegotiate)
|
||||
server->tcpStatus == CifsNeedNegotiate) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return rc;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
if (!server->session_estab) {
|
||||
memcpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8);
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <linux/random.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <uapi/linux/magic.h>
|
||||
#include <net/ipv6.h>
|
||||
#include "cifsfs.h"
|
||||
#include "cifspdu.h"
|
||||
@ -202,7 +203,7 @@ cifs_read_super(struct super_block *sb)
|
||||
sb->s_time_max = ts.tv_sec;
|
||||
}
|
||||
|
||||
sb->s_magic = CIFS_MAGIC_NUMBER;
|
||||
sb->s_magic = CIFS_SUPER_MAGIC;
|
||||
sb->s_op = &cifs_super_ops;
|
||||
sb->s_xattr = cifs_xattr_handlers;
|
||||
rc = super_setup_bdi(sb);
|
||||
@ -773,7 +774,7 @@ cifs_get_root(struct smb3_fs_context *ctx, struct super_block *sb)
|
||||
|
||||
sep = CIFS_DIR_SEP(cifs_sb);
|
||||
dentry = dget(sb->s_root);
|
||||
p = s = full_path;
|
||||
s = full_path;
|
||||
|
||||
do {
|
||||
struct inode *dir = d_inode(dentry);
|
||||
|
@ -24,8 +24,6 @@
|
||||
#include "../smbfs_common/smb2pdu.h"
|
||||
#include "smb2pdu.h"
|
||||
|
||||
#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
|
||||
|
||||
#define SMB_PATH_MAX 260
|
||||
#define CIFS_PORT 445
|
||||
#define RFC1001_PORT 139
|
||||
@ -113,7 +111,13 @@ enum statusEnum {
|
||||
CifsGood,
|
||||
CifsExiting,
|
||||
CifsNeedReconnect,
|
||||
CifsNeedNegotiate
|
||||
CifsNeedNegotiate,
|
||||
CifsInNegotiate,
|
||||
CifsNeedSessSetup,
|
||||
CifsInSessSetup,
|
||||
CifsNeedTcon,
|
||||
CifsInTcon,
|
||||
CifsInFilesInvalidate
|
||||
};
|
||||
|
||||
enum securityEnum {
|
||||
@ -263,13 +267,16 @@ struct smb_version_operations {
|
||||
/* check if we need to negotiate */
|
||||
bool (*need_neg)(struct TCP_Server_Info *);
|
||||
/* negotiate to the server */
|
||||
int (*negotiate)(const unsigned int, struct cifs_ses *);
|
||||
int (*negotiate)(const unsigned int xid,
|
||||
struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
/* set negotiated write size */
|
||||
unsigned int (*negotiate_wsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx);
|
||||
/* set negotiated read size */
|
||||
unsigned int (*negotiate_rsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx);
|
||||
/* setup smb sessionn */
|
||||
int (*sess_setup)(const unsigned int, struct cifs_ses *,
|
||||
struct TCP_Server_Info *server,
|
||||
const struct nls_table *);
|
||||
/* close smb session */
|
||||
int (*logoff)(const unsigned int, struct cifs_ses *);
|
||||
@ -414,7 +421,8 @@ struct smb_version_operations {
|
||||
void (*set_lease_key)(struct inode *, struct cifs_fid *);
|
||||
/* generate new lease key */
|
||||
void (*new_lease_key)(struct cifs_fid *);
|
||||
int (*generate_signingkey)(struct cifs_ses *);
|
||||
int (*generate_signingkey)(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *,
|
||||
bool allocate_crypto);
|
||||
int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon,
|
||||
@ -582,7 +590,7 @@ struct TCP_Server_Info {
|
||||
char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
|
||||
struct smb_version_operations *ops;
|
||||
struct smb_version_values *vals;
|
||||
/* updates to tcpStatus protected by GlobalMid_Lock */
|
||||
/* updates to tcpStatus protected by cifs_tcp_ses_lock */
|
||||
enum statusEnum tcpStatus; /* what we think the status is */
|
||||
char *hostname; /* hostname portion of UNC string */
|
||||
struct socket *ssocket;
|
||||
@ -920,7 +928,7 @@ struct cifs_ses {
|
||||
struct mutex session_mutex;
|
||||
struct TCP_Server_Info *server; /* pointer to server info */
|
||||
int ses_count; /* reference counter */
|
||||
enum statusEnum status; /* updates protected by GlobalMid_Lock */
|
||||
enum statusEnum status; /* updates protected by cifs_tcp_ses_lock */
|
||||
unsigned overrideSecFlg; /* if non-zero override global sec flags */
|
||||
char *serverOS; /* name of operating system underlying server */
|
||||
char *serverNOS; /* name of network operating system of server */
|
||||
@ -939,17 +947,13 @@ struct cifs_ses {
|
||||
struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
|
||||
enum securityEnum sectype; /* what security flavor was specified? */
|
||||
bool sign; /* is signing required? */
|
||||
bool need_reconnect:1; /* connection reset, uid now invalid */
|
||||
bool domainAuto:1;
|
||||
bool binding:1; /* are we binding the session? */
|
||||
__u16 session_flags;
|
||||
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
|
||||
__u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE];
|
||||
__u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE];
|
||||
__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
|
||||
|
||||
__u8 binding_preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
|
||||
|
||||
/*
|
||||
* Network interfaces available on the server this session is
|
||||
* connected to.
|
||||
@ -969,45 +973,34 @@ struct cifs_ses {
|
||||
spinlock_t chan_lock;
|
||||
/* ========= begin: protected by chan_lock ======== */
|
||||
#define CIFS_MAX_CHANNELS 16
|
||||
#define CIFS_ALL_CHANNELS_SET(ses) \
|
||||
((1UL << (ses)->chan_count) - 1)
|
||||
#define CIFS_ALL_CHANS_NEED_RECONNECT(ses) \
|
||||
((ses)->chans_need_reconnect == CIFS_ALL_CHANNELS_SET(ses))
|
||||
#define CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses) \
|
||||
((ses)->chans_need_reconnect = CIFS_ALL_CHANNELS_SET(ses))
|
||||
#define CIFS_CHAN_NEEDS_RECONNECT(ses, index) \
|
||||
test_bit((index), &(ses)->chans_need_reconnect)
|
||||
|
||||
struct cifs_chan chans[CIFS_MAX_CHANNELS];
|
||||
struct cifs_chan *binding_chan;
|
||||
size_t chan_count;
|
||||
size_t chan_max;
|
||||
atomic_t chan_seq; /* round robin state */
|
||||
|
||||
/*
|
||||
* chans_need_reconnect is a bitmap indicating which of the channels
|
||||
* under this smb session needs to be reconnected.
|
||||
* If not multichannel session, only one bit will be used.
|
||||
*
|
||||
* We will ask for sess and tcon reconnection only if all the
|
||||
* channels are marked for needing reconnection. This will
|
||||
* enable the sessions on top to continue to live till any
|
||||
* of the channels below are active.
|
||||
*/
|
||||
unsigned long chans_need_reconnect;
|
||||
/* ========= end: protected by chan_lock ======== */
|
||||
};
|
||||
|
||||
/*
|
||||
* When binding a new channel, we need to access the channel which isn't fully
|
||||
* established yet.
|
||||
*/
|
||||
|
||||
static inline
|
||||
struct cifs_chan *cifs_ses_binding_channel(struct cifs_ses *ses)
|
||||
{
|
||||
if (ses->binding)
|
||||
return ses->binding_chan;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the server pointer of the session. When binding a new
|
||||
* channel this returns the last channel which isn't fully established
|
||||
* yet.
|
||||
*
|
||||
* This function should be use for negprot/sess.setup codepaths. For
|
||||
* the other requests see cifs_pick_channel().
|
||||
*/
|
||||
static inline
|
||||
struct TCP_Server_Info *cifs_ses_server(struct cifs_ses *ses)
|
||||
{
|
||||
if (ses->binding)
|
||||
return ses->binding_chan->server;
|
||||
else
|
||||
return ses->server;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
cap_unix(struct cifs_ses *ses)
|
||||
{
|
||||
|
@ -2560,7 +2560,7 @@ typedef struct {
|
||||
__le32 EaSize; /* length of the xattrs */
|
||||
__u8 ShortNameLength;
|
||||
__u8 Reserved;
|
||||
__u8 ShortName[12];
|
||||
__u8 ShortName[24];
|
||||
char FileName[1];
|
||||
} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */
|
||||
|
||||
|
@ -131,7 +131,8 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
|
||||
struct smb_hdr *in_buf ,
|
||||
struct smb_hdr *out_buf,
|
||||
int *bytes_returned);
|
||||
extern int cifs_reconnect(struct TCP_Server_Info *server);
|
||||
extern int cifs_reconnect(struct TCP_Server_Info *server,
|
||||
bool mark_smb_session);
|
||||
extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr);
|
||||
extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
|
||||
extern bool backup_cred(struct cifs_sb_info *);
|
||||
@ -164,6 +165,7 @@ extern int small_smb_init_no_tc(const int smb_cmd, const int wct,
|
||||
extern enum securityEnum select_sectype(struct TCP_Server_Info *server,
|
||||
enum securityEnum requested);
|
||||
extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server,
|
||||
const struct nls_table *nls_cp);
|
||||
extern struct timespec64 cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
|
||||
extern u64 cifs_UnixTimeToNT(struct timespec64);
|
||||
@ -293,11 +295,15 @@ extern int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const struct nls_table *nlsc);
|
||||
|
||||
extern int cifs_negotiate_protocol(const unsigned int xid,
|
||||
struct cifs_ses *ses);
|
||||
struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server,
|
||||
struct nls_table *nls_info);
|
||||
extern int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required);
|
||||
extern int CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses);
|
||||
extern int CIFSSMBNegotiate(const unsigned int xid,
|
||||
struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
|
||||
extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
|
||||
const char *tree, struct cifs_tcon *tcon,
|
||||
@ -504,8 +510,10 @@ extern int cifs_verify_signature(struct smb_rqst *rqst,
|
||||
extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
|
||||
extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server);
|
||||
extern int calc_seckey(struct cifs_ses *);
|
||||
extern int generate_smb30signingkey(struct cifs_ses *);
|
||||
extern int generate_smb311signingkey(struct cifs_ses *);
|
||||
extern int generate_smb30signingkey(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
extern int generate_smb311signingkey(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
|
||||
extern int CIFSSMBCopy(unsigned int xid,
|
||||
struct cifs_tcon *source_tcon,
|
||||
@ -601,6 +609,19 @@ bool is_server_using_iface(struct TCP_Server_Info *server,
|
||||
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
|
||||
void cifs_ses_mark_for_reconnect(struct cifs_ses *ses);
|
||||
|
||||
unsigned int
|
||||
cifs_ses_get_chan_index(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
void
|
||||
cifs_chan_set_need_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
void
|
||||
cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
bool
|
||||
cifs_chan_needs_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
|
||||
void extract_unc_hostname(const char *unc, const char **h, size_t *len);
|
||||
int copy_path_name(char *dst, const char *src);
|
||||
int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
|
||||
|
@ -73,6 +73,16 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
|
||||
struct list_head *tmp;
|
||||
struct list_head *tmp1;
|
||||
|
||||
/* only send once per connect */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (tcon->ses->status != CifsGood ||
|
||||
tcon->tidStatus != CifsNeedReconnect) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return;
|
||||
}
|
||||
tcon->tidStatus = CifsInFilesInvalidate;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/* list all files open on tree connection and mark them invalid */
|
||||
spin_lock(&tcon->open_file_lock);
|
||||
list_for_each_safe(tmp, tmp1, &tcon->openFileList) {
|
||||
@ -89,6 +99,11 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
|
||||
memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid));
|
||||
mutex_unlock(&tcon->crfid.fid_mutex);
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (tcon->tidStatus == CifsInFilesInvalidate)
|
||||
tcon->tidStatus = CifsNeedTcon;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/*
|
||||
* BB Add call to invalidate_inodes(sb) for all superblocks mounted
|
||||
* to this tcon.
|
||||
@ -120,15 +135,18 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
|
||||
* only tree disconnect, open, and write, (and ulogoff which does not
|
||||
* have tcon) are allowed as we start force umount
|
||||
*/
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (tcon->tidStatus == CifsExiting) {
|
||||
if (smb_command != SMB_COM_WRITE_ANDX &&
|
||||
smb_command != SMB_COM_OPEN_ANDX &&
|
||||
smb_command != SMB_COM_TREE_DISCONNECT) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
cifs_dbg(FYI, "can not send cmd %d while umounting\n",
|
||||
smb_command);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
retries = server->nr_targets;
|
||||
|
||||
@ -148,8 +166,12 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
|
||||
}
|
||||
|
||||
/* are we still trying to reconnect? */
|
||||
if (server->tcpStatus != CifsNeedReconnect)
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus != CifsNeedReconnect) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
break;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
if (retries && --retries)
|
||||
continue;
|
||||
@ -166,31 +188,49 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
|
||||
retries = server->nr_targets;
|
||||
}
|
||||
|
||||
if (!ses->need_reconnect && !tcon->need_reconnect)
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
nls_codepage = load_nls_default();
|
||||
|
||||
/*
|
||||
* need to prevent multiple threads trying to simultaneously
|
||||
* reconnect the same SMB session
|
||||
*/
|
||||
mutex_lock(&ses->session_mutex);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsNeedReconnect) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
rc = -EHOSTDOWN;
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
goto out;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
rc = cifs_negotiate_protocol(0, ses);
|
||||
if (rc == 0 && ses->need_reconnect)
|
||||
rc = cifs_setup_session(0, ses, nls_codepage);
|
||||
/*
|
||||
* need to prevent multiple threads trying to simultaneously
|
||||
* reconnect the same SMB session
|
||||
*/
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (!cifs_chan_needs_reconnect(ses, server)) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
/* this means that we only need to tree connect */
|
||||
if (tcon->need_reconnect)
|
||||
goto skip_sess_setup;
|
||||
|
||||
rc = -EHOSTDOWN;
|
||||
goto out;
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
mutex_lock(&ses->session_mutex);
|
||||
rc = cifs_negotiate_protocol(0, ses, server);
|
||||
if (!rc)
|
||||
rc = cifs_setup_session(0, ses, server, nls_codepage);
|
||||
|
||||
/* do we need to reconnect tcon? */
|
||||
if (rc || !tcon->need_reconnect) {
|
||||
@ -198,6 +238,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
|
||||
goto out;
|
||||
}
|
||||
|
||||
skip_sess_setup:
|
||||
cifs_mark_open_files_invalid(tcon);
|
||||
rc = cifs_tree_connect(0, tcon, nls_codepage);
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
@ -337,8 +378,13 @@ static int
|
||||
smb_init_no_reconnect(int smb_command, int wct, struct cifs_tcon *tcon,
|
||||
void **request_buf, void **response_buf)
|
||||
{
|
||||
if (tcon->ses->need_reconnect || tcon->need_reconnect)
|
||||
spin_lock(&tcon->ses->chan_lock);
|
||||
if (cifs_chan_needs_reconnect(tcon->ses, tcon->ses->server) ||
|
||||
tcon->need_reconnect) {
|
||||
spin_unlock(&tcon->ses->chan_lock);
|
||||
return -EHOSTDOWN;
|
||||
}
|
||||
spin_unlock(&tcon->ses->chan_lock);
|
||||
|
||||
return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
|
||||
}
|
||||
@ -476,14 +522,15 @@ should_set_ext_sec_flag(enum securityEnum sectype)
|
||||
}
|
||||
|
||||
int
|
||||
CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
CIFSSMBNegotiate(const unsigned int xid,
|
||||
struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
NEGOTIATE_REQ *pSMB;
|
||||
NEGOTIATE_RSP *pSMBr;
|
||||
int rc = 0;
|
||||
int bytes_returned;
|
||||
int i;
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
u16 count;
|
||||
|
||||
if (!server) {
|
||||
@ -600,8 +647,12 @@ CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon)
|
||||
* the tcon is no longer on the list, so no need to take lock before
|
||||
* checking this.
|
||||
*/
|
||||
if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
|
||||
return 0;
|
||||
spin_lock(&tcon->ses->chan_lock);
|
||||
if ((tcon->need_reconnect) || CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses)) {
|
||||
spin_unlock(&tcon->ses->chan_lock);
|
||||
return -EIO;
|
||||
}
|
||||
spin_unlock(&tcon->ses->chan_lock);
|
||||
|
||||
rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
|
||||
(void **)&smb_buffer);
|
||||
@ -696,9 +747,14 @@ CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses)
|
||||
return -EIO;
|
||||
|
||||
mutex_lock(&ses->session_mutex);
|
||||
if (ses->need_reconnect)
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
goto session_already_dead; /* no need to send SMBlogoff if uid
|
||||
already closed due to reconnect */
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
|
||||
if (rc) {
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
@ -1401,7 +1457,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
|
||||
if (server->ops->is_session_expired &&
|
||||
server->ops->is_session_expired(buf)) {
|
||||
cifs_reconnect(server);
|
||||
cifs_reconnect(server, true);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -166,14 +166,17 @@ static void cifs_resolve_server(struct work_struct *work)
|
||||
* Mark all sessions and tcons for reconnect.
|
||||
*
|
||||
* @server needs to be previously set to CifsNeedReconnect.
|
||||
*
|
||||
*/
|
||||
static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server)
|
||||
static void
|
||||
cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
|
||||
bool mark_smb_session)
|
||||
{
|
||||
struct TCP_Server_Info *pserver;
|
||||
struct cifs_ses *ses;
|
||||
struct cifs_tcon *tcon;
|
||||
struct mid_q_entry *mid, *nmid;
|
||||
struct list_head retry_list;
|
||||
struct TCP_Server_Info *pserver;
|
||||
|
||||
server->maxBuf = 0;
|
||||
server->max_read = 0;
|
||||
@ -191,16 +194,37 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
|
||||
ses->need_reconnect = true;
|
||||
list_for_each_entry(tcon, &ses->tcon_list, tcon_list)
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server))
|
||||
goto next_session;
|
||||
|
||||
cifs_chan_set_need_reconnect(ses, server);
|
||||
|
||||
/* If all channels need reconnect, then tcon needs reconnect */
|
||||
if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses))
|
||||
goto next_session;
|
||||
|
||||
ses->status = CifsNeedReconnect;
|
||||
|
||||
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
|
||||
tcon->need_reconnect = true;
|
||||
tcon->tidStatus = CifsNeedReconnect;
|
||||
}
|
||||
if (ses->tcon_ipc)
|
||||
ses->tcon_ipc->need_reconnect = true;
|
||||
|
||||
next_session:
|
||||
spin_unlock(&ses->chan_lock);
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/*
|
||||
* before reconnecting the tcp session, mark the smb session (uid)
|
||||
* and the tid bad so they are not used until reconnected
|
||||
*/
|
||||
cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect and tearing down socket\n",
|
||||
__func__);
|
||||
/* do not want to be sending data on a socket we are freeing */
|
||||
cifs_dbg(FYI, "%s: tearing down socket\n", __func__);
|
||||
mutex_lock(&server->srv_mutex);
|
||||
if (server->ssocket) {
|
||||
cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", server->ssocket->state,
|
||||
@ -248,16 +272,16 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
|
||||
|
||||
static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets)
|
||||
{
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
server->nr_targets = num_targets;
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
/* the demux thread will exit normally next time through the loop */
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
wake_up(&server->response_q);
|
||||
return false;
|
||||
}
|
||||
server->tcpStatus = CifsNeedReconnect;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -268,15 +292,21 @@ static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num
|
||||
* mark all smb sessions as reconnecting for tcp session
|
||||
* reconnect tcp session
|
||||
* wake up waiters on reconnection? - (not needed currently)
|
||||
*
|
||||
* if mark_smb_session is passed as true, unconditionally mark
|
||||
* the smb session (and tcon) for reconnect as well. This value
|
||||
* doesn't really matter for non-multichannel scenario.
|
||||
*
|
||||
*/
|
||||
static int __cifs_reconnect(struct TCP_Server_Info *server)
|
||||
static int __cifs_reconnect(struct TCP_Server_Info *server,
|
||||
bool mark_smb_session)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!cifs_tcp_ses_needs_reconnect(server, 1))
|
||||
return 0;
|
||||
|
||||
cifs_mark_tcp_ses_conns_for_reconnect(server);
|
||||
cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session);
|
||||
|
||||
do {
|
||||
try_to_freeze();
|
||||
@ -299,10 +329,10 @@ static int __cifs_reconnect(struct TCP_Server_Info *server)
|
||||
} else {
|
||||
atomic_inc(&tcpSesReconnectCount);
|
||||
set_credits(server, 1);
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus != CifsExiting)
|
||||
server->tcpStatus = CifsNeedNegotiate;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
cifs_swn_reset_server_dstaddr(server);
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
}
|
||||
@ -371,7 +401,9 @@ static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int reconnect_dfs_server(struct TCP_Server_Info *server)
|
||||
static int
|
||||
reconnect_dfs_server(struct TCP_Server_Info *server,
|
||||
bool mark_smb_session)
|
||||
{
|
||||
int rc = 0;
|
||||
const char *refpath = server->current_fullpath + 1;
|
||||
@ -395,7 +427,7 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
|
||||
if (!cifs_tcp_ses_needs_reconnect(server, num_targets))
|
||||
return 0;
|
||||
|
||||
cifs_mark_tcp_ses_conns_for_reconnect(server);
|
||||
cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session);
|
||||
|
||||
do {
|
||||
try_to_freeze();
|
||||
@ -416,10 +448,10 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
|
||||
*/
|
||||
atomic_inc(&tcpSesReconnectCount);
|
||||
set_credits(server, 1);
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus != CifsExiting)
|
||||
server->tcpStatus = CifsNeedNegotiate;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
cifs_swn_reset_server_dstaddr(server);
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
} while (server->tcpStatus == CifsNeedReconnect);
|
||||
@ -430,29 +462,32 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
|
||||
dfs_cache_free_tgts(&tl);
|
||||
|
||||
/* Need to set up echo worker again once connection has been established */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsNeedNegotiate)
|
||||
mod_delayed_work(cifsiod_wq, &server->echo, 0);
|
||||
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
wake_up(&server->response_q);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cifs_reconnect(struct TCP_Server_Info *server)
|
||||
int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
|
||||
{
|
||||
/* If tcp session is not an dfs connection, then reconnect to last target server */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (!server->is_dfs_conn || !server->origin_fullpath || !server->leaf_fullpath) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return __cifs_reconnect(server);
|
||||
return __cifs_reconnect(server, mark_smb_session);
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
return reconnect_dfs_server(server);
|
||||
return reconnect_dfs_server(server, mark_smb_session);
|
||||
}
|
||||
#else
|
||||
int cifs_reconnect(struct TCP_Server_Info *server)
|
||||
int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
|
||||
{
|
||||
return __cifs_reconnect(server);
|
||||
return __cifs_reconnect(server, mark_smb_session);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -534,15 +569,18 @@ server_unresponsive(struct TCP_Server_Info *server)
|
||||
* 65s kernel_recvmsg times out, and we see that we haven't gotten
|
||||
* a response in >60s.
|
||||
*/
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if ((server->tcpStatus == CifsGood ||
|
||||
server->tcpStatus == CifsNeedNegotiate) &&
|
||||
(!server->ops->can_echo || server->ops->can_echo(server)) &&
|
||||
time_after(jiffies, server->lstrp + 3 * server->echo_interval)) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n",
|
||||
(3 * server->echo_interval) / HZ);
|
||||
cifs_reconnect(server);
|
||||
cifs_reconnect(server, false);
|
||||
return true;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -576,7 +614,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
|
||||
|
||||
/* reconnect if no credits and no requests in flight */
|
||||
if (zero_credits(server)) {
|
||||
cifs_reconnect(server);
|
||||
cifs_reconnect(server, false);
|
||||
return -ECONNABORTED;
|
||||
}
|
||||
|
||||
@ -587,13 +625,18 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
|
||||
else
|
||||
length = sock_recvmsg(server->ssocket, smb_msg, 0);
|
||||
|
||||
if (server->tcpStatus == CifsExiting)
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
if (server->tcpStatus == CifsNeedReconnect) {
|
||||
cifs_reconnect(server);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
cifs_reconnect(server, false);
|
||||
return -ECONNABORTED;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
if (length == -ERESTARTSYS ||
|
||||
length == -EAGAIN ||
|
||||
@ -610,7 +653,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
|
||||
|
||||
if (length <= 0) {
|
||||
cifs_dbg(FYI, "Received no data or error: %d\n", length);
|
||||
cifs_reconnect(server);
|
||||
cifs_reconnect(server, false);
|
||||
return -ECONNABORTED;
|
||||
}
|
||||
}
|
||||
@ -689,11 +732,11 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
|
||||
* initialize frame).
|
||||
*/
|
||||
cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT);
|
||||
cifs_reconnect(server);
|
||||
cifs_reconnect(server, true);
|
||||
break;
|
||||
default:
|
||||
cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type);
|
||||
cifs_reconnect(server);
|
||||
cifs_reconnect(server, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -771,9 +814,9 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
|
||||
cancel_delayed_work_sync(&server->echo);
|
||||
cancel_delayed_work_sync(&server->resolve);
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
server->tcpStatus = CifsExiting;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
wake_up_all(&server->response_q);
|
||||
|
||||
/* check if we have blocked requests that need to free */
|
||||
@ -866,7 +909,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
|
||||
server->vals->header_preamble_size) {
|
||||
cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
|
||||
cifs_reconnect(server);
|
||||
cifs_reconnect(server, true);
|
||||
return -ECONNABORTED;
|
||||
}
|
||||
|
||||
@ -913,7 +956,7 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
||||
|
||||
if (server->ops->is_session_expired &&
|
||||
server->ops->is_session_expired(buf)) {
|
||||
cifs_reconnect(server);
|
||||
cifs_reconnect(server, true);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1017,7 +1060,7 @@ next_pdu:
|
||||
server->vals->header_preamble_size) {
|
||||
cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n",
|
||||
server->pdu_size);
|
||||
cifs_reconnect(server);
|
||||
cifs_reconnect(server, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1069,7 +1112,7 @@ next_pdu:
|
||||
server->ops->is_status_io_timeout(buf)) {
|
||||
num_io_timeout++;
|
||||
if (num_io_timeout > NUM_STATUS_IO_TIMEOUT) {
|
||||
cifs_reconnect(server);
|
||||
cifs_reconnect(server, false);
|
||||
num_io_timeout = 0;
|
||||
continue;
|
||||
}
|
||||
@ -1390,9 +1433,9 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
|
||||
else
|
||||
cancel_delayed_work_sync(&server->reconnect);
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
server->tcpStatus = CifsExiting;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
cifs_crypto_secmech_release(server);
|
||||
|
||||
@ -1545,7 +1588,9 @@ smbd_connected:
|
||||
* to the struct since the kernel thread not created yet
|
||||
* no need to spinlock this update of tcpStatus
|
||||
*/
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
tcp_ses->tcpStatus = CifsNeedNegotiate;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
if ((ctx->max_credits < 20) || (ctx->max_credits > 60000))
|
||||
tcp_ses->max_credits = SMB2_MAX_CREDITS_AVAILABLE;
|
||||
@ -1762,15 +1807,13 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/* ses_count can never go negative */
|
||||
WARN_ON(ses->ses_count < 0);
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (ses->status == CifsGood)
|
||||
ses->status = CifsExiting;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
cifs_free_ipc(ses);
|
||||
|
||||
@ -1987,11 +2030,13 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
|
||||
cifs_dbg(FYI, "Existing smb sess found (status=%d)\n",
|
||||
ses->status);
|
||||
|
||||
mutex_lock(&ses->session_mutex);
|
||||
if (ses->need_reconnect) {
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (cifs_chan_needs_reconnect(ses, server)) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
cifs_dbg(FYI, "Session needs reconnect\n");
|
||||
|
||||
rc = cifs_negotiate_protocol(xid, ses);
|
||||
mutex_lock(&ses->session_mutex);
|
||||
rc = cifs_negotiate_protocol(xid, ses, server);
|
||||
if (rc) {
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
/* problem -- put our ses reference */
|
||||
@ -2000,7 +2045,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
rc = cifs_setup_session(xid, ses,
|
||||
rc = cifs_setup_session(xid, ses, server,
|
||||
ctx->local_nls);
|
||||
if (rc) {
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
@ -2009,9 +2054,12 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
|
||||
free_xid(xid);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
/* existing SMB ses has a server reference already */
|
||||
cifs_put_tcp_session(server, 0);
|
||||
free_xid(xid);
|
||||
@ -2060,28 +2108,33 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
|
||||
|
||||
ses->sectype = ctx->sectype;
|
||||
ses->sign = ctx->sign;
|
||||
mutex_lock(&ses->session_mutex);
|
||||
|
||||
/* add server as first channel */
|
||||
spin_lock(&ses->chan_lock);
|
||||
ses->chans[0].server = server;
|
||||
ses->chan_count = 1;
|
||||
ses->chan_max = ctx->multichannel ? ctx->max_channels:1;
|
||||
ses->chans_need_reconnect = 1;
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
rc = cifs_negotiate_protocol(xid, ses);
|
||||
mutex_lock(&ses->session_mutex);
|
||||
rc = cifs_negotiate_protocol(xid, ses, server);
|
||||
if (!rc)
|
||||
rc = cifs_setup_session(xid, ses, ctx->local_nls);
|
||||
rc = cifs_setup_session(xid, ses, server, ctx->local_nls);
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
|
||||
/* each channel uses a different signing key */
|
||||
memcpy(ses->chans[0].signkey, ses->smb3signingkey,
|
||||
sizeof(ses->smb3signingkey));
|
||||
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
if (rc)
|
||||
goto get_ses_fail;
|
||||
|
||||
/* success, put it on the list and add it as first channel */
|
||||
/*
|
||||
* success, put it on the list and add it as first channel
|
||||
* note: the session becomes active soon after this. So you'll
|
||||
* need to lock before changing something in the session.
|
||||
*/
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_add(&ses->smb_ses_list, &server->smb_ses_list);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
@ -2161,6 +2214,9 @@ cifs_put_tcon(struct cifs_tcon *tcon)
|
||||
/* tc_count can never go negative */
|
||||
WARN_ON(tcon->tc_count < 0);
|
||||
|
||||
list_del_init(&tcon->tcon_list);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
if (tcon->use_witness) {
|
||||
int rc;
|
||||
|
||||
@ -2171,9 +2227,6 @@ cifs_put_tcon(struct cifs_tcon *tcon)
|
||||
}
|
||||
}
|
||||
|
||||
list_del_init(&tcon->tcon_list);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
xid = get_xid();
|
||||
if (ses->server->ops->tree_disconnect)
|
||||
ses->server->ops->tree_disconnect(xid, tcon);
|
||||
@ -2290,10 +2343,6 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* BB Do we need to wrap session_mutex around this TCon call and Unix
|
||||
* SetFS as we do on SessSetup and reconnect?
|
||||
*/
|
||||
xid = get_xid();
|
||||
rc = ses->server->ops->tree_connect(xid, ses, ctx->UNC, tcon,
|
||||
ctx->local_nls);
|
||||
@ -3029,12 +3078,15 @@ static int mount_get_conns(struct mount_ctx *mnt_ctx)
|
||||
* for just this mount.
|
||||
*/
|
||||
reset_cifs_unix_caps(xid, tcon, cifs_sb, ctx);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) &&
|
||||
(le64_to_cpu(tcon->fsUnixInfo.Capability) &
|
||||
CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
rc = -EACCES;
|
||||
goto out;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
} else
|
||||
tcon->unix_ext = 0; /* server does not support them */
|
||||
|
||||
@ -3709,7 +3761,9 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
|
||||
if (rc == 0) {
|
||||
bool is_unicode;
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
tcon->tidStatus = CifsGood;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
tcon->need_reconnect = false;
|
||||
tcon->tid = smb_buffer_response->Tid;
|
||||
bcc_ptr = pByteArea(smb_buffer_response);
|
||||
@ -3799,26 +3853,32 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
|
||||
}
|
||||
|
||||
int
|
||||
cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)
|
||||
cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
int rc = 0;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
|
||||
if (!server->ops->need_neg || !server->ops->negotiate)
|
||||
return -ENOSYS;
|
||||
|
||||
/* only send once per connect */
|
||||
if (!server->ops->need_neg(server))
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (!server->ops->need_neg(server) ||
|
||||
server->tcpStatus != CifsNeedNegotiate) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return 0;
|
||||
}
|
||||
server->tcpStatus = CifsInNegotiate;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
rc = server->ops->negotiate(xid, ses);
|
||||
rc = server->ops->negotiate(xid, ses, server);
|
||||
if (rc == 0) {
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (server->tcpStatus == CifsNeedNegotiate)
|
||||
server->tcpStatus = CifsGood;
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsInNegotiate)
|
||||
server->tcpStatus = CifsNeedSessSetup;
|
||||
else
|
||||
rc = -EHOSTDOWN;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
}
|
||||
|
||||
return rc;
|
||||
@ -3826,12 +3886,26 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)
|
||||
|
||||
int
|
||||
cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server,
|
||||
struct nls_table *nls_info)
|
||||
{
|
||||
int rc = -ENOSYS;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
bool is_binding = false;
|
||||
|
||||
if (!ses->binding) {
|
||||
/* only send once per connect */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus != CifsNeedSessSetup) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return 0;
|
||||
}
|
||||
ses->status = CifsInSessSetup;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
if (!is_binding) {
|
||||
ses->capabilities = server->capabilities;
|
||||
if (!linuxExtEnabled)
|
||||
ses->capabilities &= (~server->vals->cap_unix);
|
||||
@ -3849,7 +3923,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
|
||||
server->sec_mode, server->capabilities, server->timeAdj);
|
||||
|
||||
if (server->ops->sess_setup)
|
||||
rc = server->ops->sess_setup(xid, ses, nls_info);
|
||||
rc = server->ops->sess_setup(xid, ses, server, nls_info);
|
||||
|
||||
if (rc)
|
||||
cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc);
|
||||
@ -4197,6 +4271,17 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
|
||||
struct dfs_cache_tgt_iterator *tit;
|
||||
bool target_match;
|
||||
|
||||
/* only send once per connect */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (tcon->ses->status != CifsGood ||
|
||||
(tcon->tidStatus != CifsNew &&
|
||||
tcon->tidStatus != CifsNeedTcon)) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return 0;
|
||||
}
|
||||
tcon->tidStatus = CifsInTcon;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
|
||||
|
||||
tit = dfs_cache_get_tgt_iterator(tl);
|
||||
@ -4355,6 +4440,17 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
|
||||
{
|
||||
const struct smb_version_operations *ops = tcon->ses->server->ops;
|
||||
|
||||
/* only send once per connect */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (tcon->ses->status != CifsGood ||
|
||||
(tcon->tidStatus != CifsNew &&
|
||||
tcon->tidStatus != CifsNeedTcon)) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return 0;
|
||||
}
|
||||
tcon->tidStatus = CifsInTcon;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
return ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc);
|
||||
}
|
||||
#endif
|
||||
|
@ -896,10 +896,10 @@ map_and_check_smb_error(struct mid_q_entry *mid, bool logErr)
|
||||
if (class == ERRSRV && code == ERRbaduid) {
|
||||
cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n",
|
||||
code);
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (mid->server->tcpStatus != CifsExiting)
|
||||
mid->server->tcpStatus = CifsNeedReconnect;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,9 @@ typedef struct _AUTHENTICATE_MESSAGE {
|
||||
int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses);
|
||||
int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, u16 *buflen,
|
||||
struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server,
|
||||
const struct nls_table *nls_cp);
|
||||
int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
|
||||
struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server,
|
||||
const struct nls_table *nls_cp);
|
||||
|
181
fs/cifs/sess.c
181
fs/cifs/sess.c
@ -65,6 +65,53 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
cifs_ses_get_chan_index(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ses->chan_count; i++) {
|
||||
if (ses->chans[i].server == server)
|
||||
return i;
|
||||
}
|
||||
|
||||
/* If we didn't find the channel, it is likely a bug */
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
cifs_chan_set_need_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
|
||||
set_bit(chan_index, &ses->chans_need_reconnect);
|
||||
cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n",
|
||||
chan_index, ses->chans_need_reconnect);
|
||||
}
|
||||
|
||||
void
|
||||
cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
|
||||
clear_bit(chan_index, &ses->chans_need_reconnect);
|
||||
cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n",
|
||||
chan_index, ses->chans_need_reconnect);
|
||||
}
|
||||
|
||||
bool
|
||||
cifs_chan_needs_reconnect(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
|
||||
return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index);
|
||||
}
|
||||
|
||||
/* returns number of channels added */
|
||||
int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
|
||||
{
|
||||
@ -261,9 +308,8 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
|
||||
chan_server = cifs_get_tcp_session(&ctx, ses->server);
|
||||
|
||||
mutex_lock(&ses->session_mutex);
|
||||
spin_lock(&ses->chan_lock);
|
||||
chan = ses->binding_chan = &ses->chans[ses->chan_count];
|
||||
chan = &ses->chans[ses->chan_count];
|
||||
chan->server = chan_server;
|
||||
if (IS_ERR(chan->server)) {
|
||||
rc = PTR_ERR(chan->server);
|
||||
@ -271,8 +317,15 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
spin_unlock(&ses->chan_lock);
|
||||
goto out;
|
||||
}
|
||||
ses->chan_count++;
|
||||
atomic_set(&ses->chan_seq, 0);
|
||||
|
||||
/* Mark this channel as needing connect/setup */
|
||||
cifs_chan_set_need_reconnect(ses, chan->server);
|
||||
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
mutex_lock(&ses->session_mutex);
|
||||
/*
|
||||
* We need to allocate the server crypto now as we will need
|
||||
* to sign packets before we generate the channel signing key
|
||||
@ -281,37 +334,29 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
rc = smb311_crypto_shash_allocate(chan->server);
|
||||
if (rc) {
|
||||
cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ses->binding = true;
|
||||
rc = cifs_negotiate_protocol(xid, ses);
|
||||
if (rc)
|
||||
goto out;
|
||||
rc = cifs_negotiate_protocol(xid, ses, chan->server);
|
||||
if (!rc)
|
||||
rc = cifs_setup_session(xid, ses, chan->server, cifs_sb->local_nls);
|
||||
|
||||
rc = cifs_setup_session(xid, ses, cifs_sb->local_nls);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/* success, put it on the list
|
||||
* XXX: sharing ses between 2 tcp servers is not possible, the
|
||||
* way "internal" linked lists works in linux makes element
|
||||
* only able to belong to one list
|
||||
*
|
||||
* the binding session is already established so the rest of
|
||||
* the code should be able to look it up, no need to add the
|
||||
* ses to the new server.
|
||||
*/
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
ses->chan_count++;
|
||||
atomic_set(&ses->chan_seq, 0);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
|
||||
out:
|
||||
ses->binding = false;
|
||||
ses->binding_chan = NULL;
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
if (rc && chan->server) {
|
||||
spin_lock(&ses->chan_lock);
|
||||
/* we rely on all bits beyond chan_count to be clear */
|
||||
cifs_chan_clear_need_reconnect(ses, chan->server);
|
||||
ses->chan_count--;
|
||||
/*
|
||||
* chan_count should never reach 0 as at least the primary
|
||||
* channel is always allocated
|
||||
*/
|
||||
WARN_ON(ses->chan_count < 1);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
}
|
||||
|
||||
if (rc && chan->server)
|
||||
cifs_put_tcp_session(chan->server, 0);
|
||||
@ -325,14 +370,16 @@ void cifs_ses_mark_for_reconnect(struct cifs_ses *ses)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ses->chan_count; i++) {
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (ses->chans[i].server->tcpStatus != CifsExiting)
|
||||
ses->chans[i].server->tcpStatus = CifsNeedReconnect;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
|
||||
static __u32 cifs_ssetup_hdr(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server,
|
||||
SESSION_SETUP_ANDX *pSMB)
|
||||
{
|
||||
__u32 capabilities = 0;
|
||||
|
||||
@ -345,7 +392,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
|
||||
pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32,
|
||||
CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4,
|
||||
USHRT_MAX));
|
||||
pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
|
||||
pSMB->req.MaxMpxCount = cpu_to_le16(server->maxReq);
|
||||
pSMB->req.VcNumber = cpu_to_le16(1);
|
||||
|
||||
/* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
|
||||
@ -356,7 +403,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
|
||||
capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
|
||||
CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
|
||||
|
||||
if (ses->server->sign)
|
||||
if (server->sign)
|
||||
pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
|
||||
|
||||
if (ses->capabilities & CAP_UNICODE) {
|
||||
@ -719,10 +766,10 @@ static inline void cifs_security_buffer_from_str(SECURITY_BUFFER *pbuf,
|
||||
int build_ntlmssp_negotiate_blob(unsigned char **pbuffer,
|
||||
u16 *buflen,
|
||||
struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server,
|
||||
const struct nls_table *nls_cp)
|
||||
{
|
||||
int rc = 0;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
NEGOTIATE_MESSAGE *sec_blob;
|
||||
__u32 flags;
|
||||
unsigned char *tmp;
|
||||
@ -776,6 +823,7 @@ setup_ntlm_neg_ret:
|
||||
int build_ntlmssp_auth_blob(unsigned char **pbuffer,
|
||||
u16 *buflen,
|
||||
struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server,
|
||||
const struct nls_table *nls_cp)
|
||||
{
|
||||
int rc;
|
||||
@ -912,6 +960,7 @@ cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
|
||||
struct sess_data {
|
||||
unsigned int xid;
|
||||
struct cifs_ses *ses;
|
||||
struct TCP_Server_Info *server;
|
||||
struct nls_table *nls_cp;
|
||||
void (*func)(struct sess_data *);
|
||||
int result;
|
||||
@ -978,30 +1027,36 @@ static int
|
||||
sess_establish_session(struct sess_data *sess_data)
|
||||
{
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = sess_data->server;
|
||||
|
||||
mutex_lock(&ses->server->srv_mutex);
|
||||
if (!ses->server->session_estab) {
|
||||
if (ses->server->sign) {
|
||||
ses->server->session_key.response =
|
||||
mutex_lock(&server->srv_mutex);
|
||||
if (!server->session_estab) {
|
||||
if (server->sign) {
|
||||
server->session_key.response =
|
||||
kmemdup(ses->auth_key.response,
|
||||
ses->auth_key.len, GFP_KERNEL);
|
||||
if (!ses->server->session_key.response) {
|
||||
mutex_unlock(&ses->server->srv_mutex);
|
||||
if (!server->session_key.response) {
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ses->server->session_key.len =
|
||||
server->session_key.len =
|
||||
ses->auth_key.len;
|
||||
}
|
||||
ses->server->sequence_number = 0x2;
|
||||
ses->server->session_estab = true;
|
||||
server->sequence_number = 0x2;
|
||||
server->session_estab = true;
|
||||
}
|
||||
mutex_unlock(&ses->server->srv_mutex);
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
|
||||
cifs_dbg(FYI, "CIFS session established successfully\n");
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
spin_lock(&ses->chan_lock);
|
||||
cifs_chan_clear_need_reconnect(ses, server);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
/* Even if one channel is active, session is in good state */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
server->tcpStatus = CifsGood;
|
||||
ses->status = CifsGood;
|
||||
ses->need_reconnect = false;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1036,6 +1091,7 @@ sess_auth_ntlmv2(struct sess_data *sess_data)
|
||||
SESSION_SETUP_ANDX *pSMB;
|
||||
char *bcc_ptr;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = sess_data->server;
|
||||
__u32 capabilities;
|
||||
__u16 bytes_remaining;
|
||||
|
||||
@ -1047,7 +1103,7 @@ sess_auth_ntlmv2(struct sess_data *sess_data)
|
||||
|
||||
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
|
||||
bcc_ptr = sess_data->iov[2].iov_base;
|
||||
capabilities = cifs_ssetup_hdr(ses, pSMB);
|
||||
capabilities = cifs_ssetup_hdr(ses, server, pSMB);
|
||||
|
||||
pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
|
||||
|
||||
@ -1145,6 +1201,7 @@ sess_auth_kerberos(struct sess_data *sess_data)
|
||||
SESSION_SETUP_ANDX *pSMB;
|
||||
char *bcc_ptr;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = sess_data->server;
|
||||
__u32 capabilities;
|
||||
__u16 bytes_remaining;
|
||||
struct key *spnego_key = NULL;
|
||||
@ -1159,9 +1216,9 @@ sess_auth_kerberos(struct sess_data *sess_data)
|
||||
|
||||
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
|
||||
bcc_ptr = sess_data->iov[2].iov_base;
|
||||
capabilities = cifs_ssetup_hdr(ses, pSMB);
|
||||
capabilities = cifs_ssetup_hdr(ses, server, pSMB);
|
||||
|
||||
spnego_key = cifs_get_spnego_key(ses);
|
||||
spnego_key = cifs_get_spnego_key(ses, server);
|
||||
if (IS_ERR(spnego_key)) {
|
||||
rc = PTR_ERR(spnego_key);
|
||||
spnego_key = NULL;
|
||||
@ -1285,12 +1342,13 @@ _sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data)
|
||||
{
|
||||
SESSION_SETUP_ANDX *pSMB;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = sess_data->server;
|
||||
__u32 capabilities;
|
||||
char *bcc_ptr;
|
||||
|
||||
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
|
||||
|
||||
capabilities = cifs_ssetup_hdr(ses, pSMB);
|
||||
capabilities = cifs_ssetup_hdr(ses, server, pSMB);
|
||||
if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) {
|
||||
cifs_dbg(VFS, "NTLMSSP requires Unicode support\n");
|
||||
return -ENOSYS;
|
||||
@ -1324,6 +1382,7 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
|
||||
struct smb_hdr *smb_buf;
|
||||
SESSION_SETUP_ANDX *pSMB;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = sess_data->server;
|
||||
__u16 bytes_remaining;
|
||||
char *bcc_ptr;
|
||||
unsigned char *ntlmsspblob = NULL;
|
||||
@ -1351,7 +1410,7 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
|
||||
|
||||
/* Build security blob before we assemble the request */
|
||||
rc = build_ntlmssp_negotiate_blob(&ntlmsspblob,
|
||||
&blob_len, ses,
|
||||
&blob_len, ses, server,
|
||||
sess_data->nls_cp);
|
||||
if (rc)
|
||||
goto out;
|
||||
@ -1426,6 +1485,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
|
||||
struct smb_hdr *smb_buf;
|
||||
SESSION_SETUP_ANDX *pSMB;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = sess_data->server;
|
||||
__u16 bytes_remaining;
|
||||
char *bcc_ptr;
|
||||
unsigned char *ntlmsspblob = NULL;
|
||||
@ -1442,7 +1502,8 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
|
||||
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
|
||||
smb_buf = (struct smb_hdr *)pSMB;
|
||||
rc = build_ntlmssp_auth_blob(&ntlmsspblob,
|
||||
&blob_len, ses, sess_data->nls_cp);
|
||||
&blob_len, ses, server,
|
||||
sess_data->nls_cp);
|
||||
if (rc)
|
||||
goto out_free_ntlmsspblob;
|
||||
sess_data->iov[1].iov_len = blob_len;
|
||||
@ -1526,11 +1587,13 @@ out:
|
||||
sess_data->result = rc;
|
||||
}
|
||||
|
||||
static int select_sec(struct cifs_ses *ses, struct sess_data *sess_data)
|
||||
static int select_sec(struct sess_data *sess_data)
|
||||
{
|
||||
int type;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = sess_data->server;
|
||||
|
||||
type = cifs_select_sectype(ses->server, ses->sectype);
|
||||
type = cifs_select_sectype(server, ses->sectype);
|
||||
cifs_dbg(FYI, "sess setup type %d\n", type);
|
||||
if (type == Unspecified) {
|
||||
cifs_dbg(VFS, "Unable to select appropriate authentication method!\n");
|
||||
@ -1561,6 +1624,7 @@ static int select_sec(struct cifs_ses *ses, struct sess_data *sess_data)
|
||||
}
|
||||
|
||||
int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server,
|
||||
const struct nls_table *nls_cp)
|
||||
{
|
||||
int rc = 0;
|
||||
@ -1575,15 +1639,16 @@ int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
|
||||
if (!sess_data)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = select_sec(ses, sess_data);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
sess_data->xid = xid;
|
||||
sess_data->ses = ses;
|
||||
sess_data->server = server;
|
||||
sess_data->buf0_type = CIFS_NO_BUFFER;
|
||||
sess_data->nls_cp = (struct nls_table *) nls_cp;
|
||||
|
||||
rc = select_sec(sess_data);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
while (sess_data->func)
|
||||
sess_data->func(sess_data);
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/vfs.h>
|
||||
#include <uapi/linux/magic.h>
|
||||
#include "cifsglob.h"
|
||||
#include "cifsproto.h"
|
||||
#include "cifs_debug.h"
|
||||
@ -163,7 +164,7 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
|
||||
{
|
||||
__u64 mid = 0;
|
||||
__u16 last_mid, cur_mid;
|
||||
bool collision;
|
||||
bool collision, reconnect = false;
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
|
||||
@ -215,7 +216,7 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
|
||||
* an eventual reconnect to clean out the pending_mid_q.
|
||||
*/
|
||||
if (num_mids > 32768)
|
||||
server->tcpStatus = CifsNeedReconnect;
|
||||
reconnect = true;
|
||||
|
||||
if (!collision) {
|
||||
mid = (__u64)cur_mid;
|
||||
@ -225,6 +226,13 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
|
||||
cur_mid++;
|
||||
}
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
|
||||
if (reconnect) {
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
server->tcpStatus = CifsNeedReconnect;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
}
|
||||
|
||||
return mid;
|
||||
}
|
||||
|
||||
@ -414,14 +422,16 @@ cifs_need_neg(struct TCP_Server_Info *server)
|
||||
}
|
||||
|
||||
static int
|
||||
cifs_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
cifs_negotiate(const unsigned int xid,
|
||||
struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
int rc;
|
||||
rc = CIFSSMBNegotiate(xid, ses);
|
||||
rc = CIFSSMBNegotiate(xid, ses, server);
|
||||
if (rc == -EAGAIN) {
|
||||
/* retry only once on 1st time connection */
|
||||
set_credits(ses->server, 1);
|
||||
rc = CIFSSMBNegotiate(xid, ses);
|
||||
set_credits(server, 1);
|
||||
rc = CIFSSMBNegotiate(xid, ses, server);
|
||||
if (rc == -EAGAIN)
|
||||
rc = -EHOSTDOWN;
|
||||
}
|
||||
@ -878,7 +888,7 @@ cifs_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
{
|
||||
int rc = -EOPNOTSUPP;
|
||||
|
||||
buf->f_type = CIFS_MAGIC_NUMBER;
|
||||
buf->f_type = CIFS_SUPER_MAGIC;
|
||||
|
||||
/*
|
||||
* We could add a second check for a QFS Unix capability bit
|
||||
|
@ -13,8 +13,6 @@
|
||||
#ifndef _SMB2_GLOB_H
|
||||
#define _SMB2_GLOB_H
|
||||
|
||||
#define SMB2_MAGIC_NUMBER 0xFE534D42
|
||||
|
||||
/*
|
||||
*****************************************************************
|
||||
* Constants go here
|
||||
|
@ -847,16 +847,17 @@ smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *serve
|
||||
* SMB2 header.
|
||||
*
|
||||
* @ses: server session structure
|
||||
* @server: pointer to server info
|
||||
* @iov: array containing the SMB request we will send to the server
|
||||
* @nvec: number of array entries for the iov
|
||||
*/
|
||||
int
|
||||
smb311_update_preauth_hash(struct cifs_ses *ses, struct kvec *iov, int nvec)
|
||||
smb311_update_preauth_hash(struct cifs_ses *ses, struct TCP_Server_Info *server,
|
||||
struct kvec *iov, int nvec)
|
||||
{
|
||||
int i, rc;
|
||||
struct sdesc *d;
|
||||
struct smb2_hdr *hdr;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
|
||||
hdr = (struct smb2_hdr *)iov[0].iov_base;
|
||||
/* neg prot are always taken */
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/sort.h>
|
||||
#include <crypto/aead.h>
|
||||
#include <linux/fiemap.h>
|
||||
#include <uapi/linux/magic.h>
|
||||
#include "cifsfs.h"
|
||||
#include "cifsglob.h"
|
||||
#include "smb2pdu.h"
|
||||
@ -121,9 +122,13 @@ smb2_add_credits(struct TCP_Server_Info *server,
|
||||
optype, scredits, add);
|
||||
}
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsNeedReconnect
|
||||
|| server->tcpStatus == CifsExiting)
|
||||
|| server->tcpStatus == CifsExiting) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
switch (rc) {
|
||||
case -1:
|
||||
@ -208,11 +213,15 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
|
||||
return rc;
|
||||
spin_lock(&server->req_lock);
|
||||
} else {
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
spin_unlock(&server->req_lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
spin_lock(&server->req_lock);
|
||||
scredits = server->credits;
|
||||
/* can deadlock with reopen */
|
||||
if (scredits <= 8) {
|
||||
@ -384,14 +393,16 @@ smb2_need_neg(struct TCP_Server_Info *server)
|
||||
}
|
||||
|
||||
static int
|
||||
smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
smb2_negotiate(const unsigned int xid,
|
||||
struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
int rc;
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
cifs_ses_server(ses)->CurrentMid = 0;
|
||||
server->CurrentMid = 0;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
rc = SMB2_negotiate(xid, ses);
|
||||
rc = SMB2_negotiate(xid, ses, server);
|
||||
/* BB we probably don't need to retry with modern servers */
|
||||
if (rc == -EAGAIN)
|
||||
rc = -EHOSTDOWN;
|
||||
@ -2747,7 +2758,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
goto qfs_exit;
|
||||
|
||||
rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
|
||||
buf->f_type = SMB2_MAGIC_NUMBER;
|
||||
buf->f_type = SMB2_SUPER_MAGIC;
|
||||
info = (struct smb2_fs_full_size_info *)(
|
||||
le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp);
|
||||
rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
|
||||
@ -2789,7 +2800,7 @@ smb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
rc = SMB311_posix_qfs_info(xid, tcon, fid.persistent_fid,
|
||||
fid.volatile_fid, buf);
|
||||
buf->f_type = SMB2_MAGIC_NUMBER;
|
||||
buf->f_type = SMB2_SUPER_MAGIC;
|
||||
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
|
||||
return rc;
|
||||
}
|
||||
@ -4808,7 +4819,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
|
||||
if (server->ops->is_session_expired &&
|
||||
server->ops->is_session_expired(buf)) {
|
||||
if (!is_offloaded)
|
||||
cifs_reconnect(server);
|
||||
cifs_reconnect(server, true);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -4981,10 +4992,12 @@ static void smb2_decrypt_offload(struct work_struct *work)
|
||||
|
||||
mid->callback(mid);
|
||||
} else {
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (dw->server->tcpStatus == CifsNeedReconnect) {
|
||||
mid->mid_state = MID_RETRY_NEEDED;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
mid->callback(mid);
|
||||
} else {
|
||||
mid->mid_state = MID_REQUEST_SUBMITTED;
|
||||
@ -4992,6 +5005,7 @@ static void smb2_decrypt_offload(struct work_struct *work)
|
||||
list_add_tail(&mid->qhead,
|
||||
&dw->server->pending_mid_q);
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
}
|
||||
}
|
||||
cifs_mid_q_entry_release(mid);
|
||||
@ -5221,13 +5235,13 @@ smb3_receive_transform(struct TCP_Server_Info *server,
|
||||
sizeof(struct smb2_hdr)) {
|
||||
cifs_server_dbg(VFS, "Transform message is too small (%u)\n",
|
||||
pdu_length);
|
||||
cifs_reconnect(server);
|
||||
cifs_reconnect(server, true);
|
||||
return -ECONNABORTED;
|
||||
}
|
||||
|
||||
if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) {
|
||||
cifs_server_dbg(VFS, "Transform message is broken\n");
|
||||
cifs_reconnect(server);
|
||||
cifs_reconnect(server, true);
|
||||
return -ECONNABORTED;
|
||||
}
|
||||
|
||||
|
@ -162,6 +162,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL)
|
||||
return 0;
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (tcon->tidStatus == CifsExiting) {
|
||||
/*
|
||||
* only tree disconnect, open, and write,
|
||||
@ -171,11 +172,13 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
if ((smb2_command != SMB2_WRITE) &&
|
||||
(smb2_command != SMB2_CREATE) &&
|
||||
(smb2_command != SMB2_TREE_DISCONNECT)) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
cifs_dbg(FYI, "can not send cmd %d while umounting\n",
|
||||
smb2_command);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
if ((!tcon->ses) || (tcon->ses->status == CifsExiting) ||
|
||||
(!tcon->ses->server) || !server)
|
||||
return -EIO;
|
||||
@ -214,8 +217,12 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
}
|
||||
|
||||
/* are we still trying to reconnect? */
|
||||
if (server->tcpStatus != CifsNeedReconnect)
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus != CifsNeedReconnect) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
break;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
if (retries && --retries)
|
||||
continue;
|
||||
@ -232,64 +239,70 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
|
||||
retries = server->nr_targets;
|
||||
}
|
||||
|
||||
if (!tcon->ses->need_reconnect && !tcon->need_reconnect)
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d",
|
||||
tcon->ses->chans_need_reconnect,
|
||||
tcon->need_reconnect);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
nls_codepage = load_nls_default();
|
||||
|
||||
/*
|
||||
* need to prevent multiple threads trying to simultaneously reconnect
|
||||
* the same SMB session
|
||||
*/
|
||||
mutex_lock(&tcon->ses->session_mutex);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsNeedReconnect) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
rc = -EHOSTDOWN;
|
||||
mutex_unlock(&tcon->ses->session_mutex);
|
||||
goto out;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/*
|
||||
* If we are reconnecting an extra channel, bind
|
||||
* need to prevent multiple threads trying to simultaneously
|
||||
* reconnect the same SMB session
|
||||
*/
|
||||
if (CIFS_SERVER_IS_CHAN(server)) {
|
||||
ses->binding = true;
|
||||
ses->binding_chan = cifs_ses_find_chan(ses, server);
|
||||
}
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (!cifs_chan_needs_reconnect(ses, server)) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
rc = cifs_negotiate_protocol(0, tcon->ses);
|
||||
if (!rc && tcon->ses->need_reconnect) {
|
||||
rc = cifs_setup_session(0, tcon->ses, nls_codepage);
|
||||
/* this means that we only need to tree connect */
|
||||
if (tcon->need_reconnect)
|
||||
goto skip_sess_setup;
|
||||
|
||||
goto out;
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
mutex_lock(&ses->session_mutex);
|
||||
rc = cifs_negotiate_protocol(0, ses, server);
|
||||
if (!rc) {
|
||||
rc = cifs_setup_session(0, ses, server, nls_codepage);
|
||||
if ((rc == -EACCES) && !tcon->retry) {
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
rc = -EHOSTDOWN;
|
||||
ses->binding = false;
|
||||
ses->binding_chan = NULL;
|
||||
mutex_unlock(&tcon->ses->session_mutex);
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* End of channel binding
|
||||
*/
|
||||
ses->binding = false;
|
||||
ses->binding_chan = NULL;
|
||||
|
||||
if (rc || !tcon->need_reconnect) {
|
||||
mutex_unlock(&tcon->ses->session_mutex);
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
goto out;
|
||||
}
|
||||
|
||||
skip_sess_setup:
|
||||
cifs_mark_open_files_invalid(tcon);
|
||||
if (tcon->use_persistent)
|
||||
tcon->need_reopen_files = true;
|
||||
|
||||
rc = cifs_tree_connect(0, tcon, nls_codepage);
|
||||
mutex_unlock(&tcon->ses->session_mutex);
|
||||
mutex_unlock(&ses->session_mutex);
|
||||
|
||||
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
|
||||
if (rc) {
|
||||
@ -833,7 +846,9 @@ add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode)
|
||||
*/
|
||||
|
||||
int
|
||||
SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
SMB2_negotiate(const unsigned int xid,
|
||||
struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
{
|
||||
struct smb_rqst rqst;
|
||||
struct smb2_negotiate_req *req;
|
||||
@ -842,7 +857,6 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
|
||||
struct kvec rsp_iov;
|
||||
int rc = 0;
|
||||
int resp_buftype;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
int blob_offset, blob_length;
|
||||
char *security_blob;
|
||||
int flags = CIFS_NEG_OP;
|
||||
@ -1221,6 +1235,7 @@ smb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
|
||||
struct SMB2_sess_data {
|
||||
unsigned int xid;
|
||||
struct cifs_ses *ses;
|
||||
struct TCP_Server_Info *server;
|
||||
struct nls_table *nls_cp;
|
||||
void (*func)(struct SMB2_sess_data *);
|
||||
int result;
|
||||
@ -1242,9 +1257,10 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int rc;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = sess_data->server;
|
||||
struct smb2_sess_setup_req *req;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
unsigned int total_len;
|
||||
bool is_binding = false;
|
||||
|
||||
rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, server,
|
||||
(void **) &req,
|
||||
@ -1252,11 +1268,16 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (sess_data->ses->binding) {
|
||||
req->hdr.SessionId = cpu_to_le64(sess_data->ses->Suid);
|
||||
spin_lock(&ses->chan_lock);
|
||||
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
if (is_binding) {
|
||||
req->hdr.SessionId = cpu_to_le64(ses->Suid);
|
||||
req->hdr.Flags |= SMB2_FLAGS_SIGNED;
|
||||
req->PreviousSessionId = 0;
|
||||
req->Flags = SMB2_SESSION_REQ_FLAG_BINDING;
|
||||
cifs_dbg(FYI, "Binding to sess id: %llx\n", ses->Suid);
|
||||
} else {
|
||||
/* First session, not a reauthenticate */
|
||||
req->hdr.SessionId = 0;
|
||||
@ -1266,6 +1287,8 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
|
||||
*/
|
||||
req->PreviousSessionId = cpu_to_le64(sess_data->previous_session);
|
||||
req->Flags = 0; /* MBZ */
|
||||
cifs_dbg(FYI, "Fresh session. Previous: %llx\n",
|
||||
sess_data->previous_session);
|
||||
}
|
||||
|
||||
/* enough to enable echos and oplocks and one max size write */
|
||||
@ -1325,7 +1348,7 @@ SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data)
|
||||
|
||||
/* BB add code to build os and lm fields */
|
||||
rc = cifs_send_recv(sess_data->xid, sess_data->ses,
|
||||
cifs_ses_server(sess_data->ses),
|
||||
sess_data->server,
|
||||
&rqst,
|
||||
&sess_data->buf0_type,
|
||||
CIFS_LOG_ERROR | CIFS_SESS_OP, &rsp_iov);
|
||||
@ -1340,11 +1363,11 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int rc = 0;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
struct TCP_Server_Info *server = sess_data->server;
|
||||
|
||||
mutex_lock(&server->srv_mutex);
|
||||
if (server->ops->generate_signingkey) {
|
||||
rc = server->ops->generate_signingkey(ses);
|
||||
rc = server->ops->generate_signingkey(ses, server);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI,
|
||||
"SMB3 session key generation failed\n");
|
||||
@ -1359,13 +1382,16 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
|
||||
cifs_dbg(FYI, "SMB2/3 session established successfully\n");
|
||||
/* keep existing ses state if binding */
|
||||
if (!ses->binding) {
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
cifs_chan_clear_need_reconnect(ses, server);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
/* Even if one channel is active, session is in good state */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
server->tcpStatus = CifsGood;
|
||||
ses->status = CifsGood;
|
||||
ses->need_reconnect = false;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -1376,15 +1402,17 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int rc;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = sess_data->server;
|
||||
struct cifs_spnego_msg *msg;
|
||||
struct key *spnego_key = NULL;
|
||||
struct smb2_sess_setup_rsp *rsp = NULL;
|
||||
bool is_binding = false;
|
||||
|
||||
rc = SMB2_sess_alloc_buffer(sess_data);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
spnego_key = cifs_get_spnego_key(ses);
|
||||
spnego_key = cifs_get_spnego_key(ses, server);
|
||||
if (IS_ERR(spnego_key)) {
|
||||
rc = PTR_ERR(spnego_key);
|
||||
if (rc == -ENOKEY)
|
||||
@ -1405,8 +1433,12 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
|
||||
goto out_put_spnego_key;
|
||||
}
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
/* keep session key if binding */
|
||||
if (!ses->binding) {
|
||||
if (!is_binding) {
|
||||
ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
|
||||
GFP_KERNEL);
|
||||
if (!ses->auth_key.response) {
|
||||
@ -1427,7 +1459,7 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
|
||||
|
||||
rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
|
||||
/* keep session id and flags if binding */
|
||||
if (!ses->binding) {
|
||||
if (!is_binding) {
|
||||
ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
|
||||
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
||||
}
|
||||
@ -1459,10 +1491,12 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int rc;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = sess_data->server;
|
||||
struct smb2_sess_setup_rsp *rsp = NULL;
|
||||
unsigned char *ntlmssp_blob = NULL;
|
||||
bool use_spnego = false; /* else use raw ntlmssp */
|
||||
u16 blob_length = 0;
|
||||
bool is_binding = false;
|
||||
|
||||
/*
|
||||
* If memory allocation is successful, caller of this function
|
||||
@ -1480,7 +1514,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
|
||||
goto out_err;
|
||||
|
||||
rc = build_ntlmssp_negotiate_blob(&ntlmssp_blob,
|
||||
&blob_length, ses,
|
||||
&blob_length, ses, server,
|
||||
sess_data->nls_cp);
|
||||
if (rc)
|
||||
goto out_err;
|
||||
@ -1519,8 +1553,12 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
|
||||
|
||||
cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
/* keep existing ses id and flags if binding */
|
||||
if (!ses->binding) {
|
||||
if (!is_binding) {
|
||||
ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
|
||||
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
||||
}
|
||||
@ -1545,11 +1583,13 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int rc;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = sess_data->server;
|
||||
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;
|
||||
bool is_binding = false;
|
||||
|
||||
rc = SMB2_sess_alloc_buffer(sess_data);
|
||||
if (rc)
|
||||
@ -1558,7 +1598,8 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
|
||||
req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base;
|
||||
req->hdr.SessionId = cpu_to_le64(ses->Suid);
|
||||
|
||||
rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
|
||||
rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length,
|
||||
ses, server,
|
||||
sess_data->nls_cp);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc);
|
||||
@ -1580,8 +1621,12 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
|
||||
|
||||
rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
/* keep existing ses id and flags if binding */
|
||||
if (!ses->binding) {
|
||||
if (!is_binding) {
|
||||
ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
|
||||
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
|
||||
}
|
||||
@ -1612,11 +1657,13 @@ out:
|
||||
}
|
||||
|
||||
static int
|
||||
SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
|
||||
SMB2_select_sec(struct SMB2_sess_data *sess_data)
|
||||
{
|
||||
int type;
|
||||
struct cifs_ses *ses = sess_data->ses;
|
||||
struct TCP_Server_Info *server = sess_data->server;
|
||||
|
||||
type = smb2_select_sectype(cifs_ses_server(ses), ses->sectype);
|
||||
type = smb2_select_sectype(server, ses->sectype);
|
||||
cifs_dbg(FYI, "sess setup type %d\n", type);
|
||||
if (type == Unspecified) {
|
||||
cifs_dbg(VFS, "Unable to select appropriate authentication method!\n");
|
||||
@ -1640,10 +1687,10 @@ SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
|
||||
|
||||
int
|
||||
SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server,
|
||||
const struct nls_table *nls_cp)
|
||||
{
|
||||
int rc = 0;
|
||||
struct TCP_Server_Info *server = cifs_ses_server(ses);
|
||||
struct SMB2_sess_data *sess_data;
|
||||
|
||||
cifs_dbg(FYI, "Session Setup\n");
|
||||
@ -1657,15 +1704,17 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
|
||||
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->server = server;
|
||||
sess_data->buf0_type = CIFS_NO_BUFFER;
|
||||
sess_data->nls_cp = (struct nls_table *) nls_cp;
|
||||
sess_data->previous_session = ses->Suid;
|
||||
|
||||
rc = SMB2_select_sec(sess_data);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Initialize the session hash with the server one.
|
||||
*/
|
||||
@ -1704,8 +1753,12 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
|
||||
return -EIO;
|
||||
|
||||
/* no need to send SMB logoff if uid already closed due to reconnect */
|
||||
if (ses->need_reconnect)
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
goto smb2_session_already_dead;
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
rc = smb2_plain_req_init(SMB2_LOGOFF, NULL, ses->server,
|
||||
(void **) &req, &total_len);
|
||||
@ -1867,7 +1920,9 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
|
||||
tcon->share_flags = le32_to_cpu(rsp->ShareFlags);
|
||||
tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */
|
||||
tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
tcon->tidStatus = CifsGood;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
tcon->need_reconnect = false;
|
||||
tcon->tid = le32_to_cpu(rsp->hdr.Id.SyncId.TreeId);
|
||||
strlcpy(tcon->treeName, tree, sizeof(tcon->treeName));
|
||||
@ -1913,8 +1968,13 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
|
||||
if (!ses || !(ses->server))
|
||||
return -EIO;
|
||||
|
||||
if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
|
||||
spin_lock(&ses->chan_lock);
|
||||
if ((tcon->need_reconnect) ||
|
||||
(CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses))) {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
close_cached_dir_lease(&tcon->crfid);
|
||||
|
||||
@ -3797,13 +3857,16 @@ SMB2_echo(struct TCP_Server_Info *server)
|
||||
.rq_nvec = 1 };
|
||||
unsigned int total_len;
|
||||
|
||||
cifs_dbg(FYI, "In echo request\n");
|
||||
cifs_dbg(FYI, "In echo request for conn_id %lld\n", server->conn_id);
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsNeedNegotiate) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
/* No need to send echo on newly established connections */
|
||||
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
|
||||
return rc;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
rc = smb2_plain_req_init(SMB2_ECHO, NULL, server,
|
||||
(void **)&req, &total_len);
|
||||
|
@ -123,8 +123,11 @@ extern void smb2_set_related(struct smb_rqst *rqst);
|
||||
* SMB2 Worker functions - most of protocol specific implementation details
|
||||
* are contained within these calls.
|
||||
*/
|
||||
extern int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses);
|
||||
extern int SMB2_negotiate(const unsigned int xid,
|
||||
struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server);
|
||||
extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server,
|
||||
const struct nls_table *nls_cp);
|
||||
extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses);
|
||||
extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses,
|
||||
@ -276,6 +279,7 @@ extern void smb2_copy_fs_info_to_kstatfs(
|
||||
struct kstatfs *kst);
|
||||
extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server);
|
||||
extern int smb311_update_preauth_hash(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server,
|
||||
struct kvec *iov, int nvec);
|
||||
extern int smb2_query_info_compound(const unsigned int xid,
|
||||
struct cifs_tcon *tcon,
|
||||
|
@ -100,7 +100,8 @@ int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
|
||||
goto out;
|
||||
|
||||
found:
|
||||
if (ses->binding) {
|
||||
if (cifs_chan_needs_reconnect(ses, server) &&
|
||||
!CIFS_ALL_CHANS_NEED_RECONNECT(ses)) {
|
||||
/*
|
||||
* If we are in the process of binding a new channel
|
||||
* to an existing session, use the master connection
|
||||
@ -390,12 +391,18 @@ struct derivation_triplet {
|
||||
|
||||
static int
|
||||
generate_smb3signingkey(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server,
|
||||
const struct derivation_triplet *ptriplet)
|
||||
{
|
||||
int rc;
|
||||
#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS
|
||||
struct TCP_Server_Info *server = ses->server;
|
||||
#endif
|
||||
bool is_binding = false;
|
||||
int chan_index = 0;
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
|
||||
chan_index = cifs_ses_get_chan_index(ses, server);
|
||||
/* TODO: introduce ref counting for channels when the can be freed */
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
/*
|
||||
* All channels use the same encryption/decryption keys but
|
||||
@ -407,10 +414,10 @@ generate_smb3signingkey(struct cifs_ses *ses,
|
||||
* master connection signing key stored in the session
|
||||
*/
|
||||
|
||||
if (ses->binding) {
|
||||
if (is_binding) {
|
||||
rc = generate_key(ses, ptriplet->signing.label,
|
||||
ptriplet->signing.context,
|
||||
cifs_ses_binding_channel(ses)->signkey,
|
||||
ses->chans[chan_index].signkey,
|
||||
SMB3_SIGN_KEY_SIZE);
|
||||
if (rc)
|
||||
return rc;
|
||||
@ -422,6 +429,7 @@ generate_smb3signingkey(struct cifs_ses *ses,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* safe to access primary channel, since it will never go away */
|
||||
memcpy(ses->chans[0].signkey, ses->smb3signingkey,
|
||||
SMB3_SIGN_KEY_SIZE);
|
||||
|
||||
@ -470,7 +478,8 @@ generate_smb3signingkey(struct cifs_ses *ses,
|
||||
}
|
||||
|
||||
int
|
||||
generate_smb30signingkey(struct cifs_ses *ses)
|
||||
generate_smb30signingkey(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
|
||||
{
|
||||
struct derivation_triplet triplet;
|
||||
@ -494,11 +503,12 @@ generate_smb30signingkey(struct cifs_ses *ses)
|
||||
d->context.iov_base = "ServerOut";
|
||||
d->context.iov_len = 10;
|
||||
|
||||
return generate_smb3signingkey(ses, &triplet);
|
||||
return generate_smb3signingkey(ses, server, &triplet);
|
||||
}
|
||||
|
||||
int
|
||||
generate_smb311signingkey(struct cifs_ses *ses)
|
||||
generate_smb311signingkey(struct cifs_ses *ses,
|
||||
struct TCP_Server_Info *server)
|
||||
|
||||
{
|
||||
struct derivation_triplet triplet;
|
||||
@ -522,7 +532,7 @@ generate_smb311signingkey(struct cifs_ses *ses)
|
||||
d->context.iov_base = ses->preauth_sha_hash;
|
||||
d->context.iov_len = 64;
|
||||
|
||||
return generate_smb3signingkey(ses, &triplet);
|
||||
return generate_smb3signingkey(ses, server, &triplet);
|
||||
}
|
||||
|
||||
int
|
||||
@ -624,8 +634,12 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
|
||||
|
||||
if (!is_signed)
|
||||
return 0;
|
||||
if (server->tcpStatus == CifsNeedNegotiate)
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsNeedNegotiate) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
if (!is_binding && !server->session_estab) {
|
||||
strncpy(shdr->Signature, "BSRSPYL", 8);
|
||||
return 0;
|
||||
@ -741,30 +755,41 @@ static int
|
||||
smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
|
||||
struct smb2_hdr *shdr, struct mid_q_entry **mid)
|
||||
{
|
||||
if (server->tcpStatus == CifsExiting)
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (server->tcpStatus == CifsNeedReconnect) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (server->tcpStatus == CifsNeedNegotiate &&
|
||||
shdr->Command != SMB2_NEGOTIATE)
|
||||
shdr->Command != SMB2_NEGOTIATE) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (ses->status == CifsNew) {
|
||||
if ((shdr->Command != SMB2_SESSION_SETUP) &&
|
||||
(shdr->Command != SMB2_NEGOTIATE))
|
||||
(shdr->Command != SMB2_NEGOTIATE)) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
/* else ok - we are setting up session */
|
||||
}
|
||||
|
||||
if (ses->status == CifsExiting) {
|
||||
if (shdr->Command != SMB2_LOGOFF)
|
||||
if (shdr->Command != SMB2_LOGOFF) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
/* else ok - we are shutting down the session */
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
*mid = smb2_mid_entry_alloc(shdr, server);
|
||||
if (*mid == NULL)
|
||||
@ -837,9 +862,13 @@ smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
|
||||
(struct smb2_hdr *)rqst->rq_iov[0].iov_base;
|
||||
struct mid_q_entry *mid;
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsNeedNegotiate &&
|
||||
shdr->Command != SMB2_NEGOTIATE)
|
||||
shdr->Command != SMB2_NEGOTIATE) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return ERR_PTR(-EAGAIN);
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
smb2_seq_num_into_buf(server, shdr);
|
||||
|
||||
|
@ -430,9 +430,9 @@ unmask:
|
||||
* be taken as the remainder of this one. We need to kill the
|
||||
* socket so the server throws away the partial SMB
|
||||
*/
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
server->tcpStatus = CifsNeedReconnect;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
trace_smb3_partial_send_reconnect(server->CurrentMid,
|
||||
server->conn_id, server->hostname);
|
||||
}
|
||||
@ -578,10 +578,14 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
|
||||
return -ERESTARTSYS;
|
||||
spin_lock(&server->req_lock);
|
||||
} else {
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
spin_unlock(&server->req_lock);
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/*
|
||||
* For normal commands, reserve the last MAX_COMPOUND
|
||||
@ -596,6 +600,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
|
||||
* for servers that are slow to hand out credits on
|
||||
* new sessions.
|
||||
*/
|
||||
spin_lock(&server->req_lock);
|
||||
if (!optype && num_credits == 1 &&
|
||||
server->in_flight > 2 * MAX_COMPOUND &&
|
||||
*credits <= MAX_COMPOUND) {
|
||||
@ -723,28 +728,36 @@ cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
|
||||
static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
|
||||
struct mid_q_entry **ppmidQ)
|
||||
{
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (ses->server->tcpStatus == CifsExiting) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (ses->server->tcpStatus == CifsNeedReconnect) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (ses->status == CifsNew) {
|
||||
if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
|
||||
(in_buf->Command != SMB_COM_NEGOTIATE))
|
||||
(in_buf->Command != SMB_COM_NEGOTIATE)) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
/* else ok - we are setting up session */
|
||||
}
|
||||
|
||||
if (ses->status == CifsExiting) {
|
||||
/* check if SMB session is bad because we are setting it up */
|
||||
if (in_buf->Command != SMB_COM_LOGOFF_ANDX)
|
||||
if (in_buf->Command != SMB_COM_LOGOFF_ANDX) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
/* else ok - we are shutting down session */
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
*ppmidQ = AllocMidQEntry(in_buf, ses->server);
|
||||
if (*ppmidQ == NULL)
|
||||
@ -1044,19 +1057,11 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
|
||||
if (!ses)
|
||||
return NULL;
|
||||
|
||||
spin_lock(&ses->chan_lock);
|
||||
if (!ses->binding) {
|
||||
/* round robin */
|
||||
if (ses->chan_count > 1) {
|
||||
index = (uint)atomic_inc_return(&ses->chan_seq);
|
||||
index %= ses->chan_count;
|
||||
}
|
||||
spin_unlock(&ses->chan_lock);
|
||||
|
||||
return ses->chans[index].server;
|
||||
} else {
|
||||
spin_unlock(&ses->chan_lock);
|
||||
return cifs_ses_server(ses);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
@ -1084,8 +1089,12 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (server->tcpStatus == CifsExiting)
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/*
|
||||
* Wait for all the requests to become available.
|
||||
@ -1188,12 +1197,17 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
||||
/*
|
||||
* Compounding is never used during session establish.
|
||||
*/
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
mutex_lock(&server->srv_mutex);
|
||||
smb311_update_preauth_hash(ses, rqst[0].rq_iov,
|
||||
rqst[0].rq_nvec);
|
||||
smb311_update_preauth_hash(ses, server, rqst[0].rq_iov, rqst[0].rq_nvec);
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
for (i = 0; i < num_rqst; i++) {
|
||||
rc = wait_for_response(server, midQ[i]);
|
||||
@ -1256,15 +1270,19 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
||||
/*
|
||||
* Compounding is never used during session establish.
|
||||
*/
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) {
|
||||
struct kvec iov = {
|
||||
.iov_base = resp_iov[0].iov_base,
|
||||
.iov_len = resp_iov[0].iov_len
|
||||
};
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
mutex_lock(&server->srv_mutex);
|
||||
smb311_update_preauth_hash(ses, &iov, 1);
|
||||
smb311_update_preauth_hash(ses, server, &iov, 1);
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
out:
|
||||
/*
|
||||
@ -1353,8 +1371,12 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (server->tcpStatus == CifsExiting)
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/* Ensure that we do not send more than 50 overlapping requests
|
||||
to the same server. We may make this configurable later or
|
||||
@ -1494,8 +1516,12 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (server->tcpStatus == CifsExiting)
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->tcpStatus == CifsExiting) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return -ENOENT;
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/* Ensure that we do not send more than 50 overlapping requests
|
||||
to the same server. We may make this configurable later or
|
||||
@ -1553,10 +1579,12 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
(server->tcpStatus != CifsNew)));
|
||||
|
||||
/* Were we interrupted by a signal ? */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if ((rc == -ERESTARTSYS) &&
|
||||
(midQ->mid_state == MID_REQUEST_SUBMITTED) &&
|
||||
((server->tcpStatus == CifsGood) ||
|
||||
(server->tcpStatus == CifsNew))) {
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
if (in_buf->Command == SMB_COM_TRANSACTION2) {
|
||||
/* POSIX lock. We send a NT_CANCEL SMB to cause the
|
||||
@ -1595,7 +1623,9 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
|
||||
/* We got the response - restart system call. */
|
||||
rstart = 1;
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
rc = cifs_sync_mid_result(midQ, server);
|
||||
if (rc != 0)
|
||||
|
@ -52,6 +52,7 @@
|
||||
#define QNX6_SUPER_MAGIC 0x68191122 /* qnx6 fs detection */
|
||||
#define AFS_FS_MAGIC 0x6B414653
|
||||
|
||||
|
||||
#define REISERFS_SUPER_MAGIC 0x52654973 /* used by gcc */
|
||||
/* used by file system utilities that
|
||||
look at the superblock, etc. */
|
||||
@ -60,6 +61,9 @@
|
||||
#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
|
||||
|
||||
#define SMB_SUPER_MAGIC 0x517B
|
||||
#define CIFS_SUPER_MAGIC 0xFF534D42 /* the first four bytes of SMB PDUs */
|
||||
#define SMB2_SUPER_MAGIC 0xFE534D42
|
||||
|
||||
#define CGROUP_SUPER_MAGIC 0x27e0eb
|
||||
#define CGROUP2_SUPER_MAGIC 0x63677270
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user