[linux-cifs-client] use non-zero vcnumbers patch

Jeff Layton jlayton at redhat.com
Wed Feb 4 01:49:23 GMT 2009


On Fri, 30 Jan 2009 19:50:08 -0600
Steve French <smfrench at gmail.com> wrote:

> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 94c1ca0..e004f6d 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -164,9 +164,12 @@ struct TCP_Server_Info {
>  	/* multiplexed reads or writes */
>  	unsigned int maxBuf;	/* maxBuf specifies the maximum */
>  	/* message size the server can send or receive for non-raw SMBs */
> -	unsigned int maxRw;	/* maxRw specifies the maximum */
> +	unsigned int max_rw;	/* maxRw specifies the maximum */
>  	/* message size the server can send or receive for */
>  	/* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */
> +	unsigned int max_vcs;	/* maximum number of smb sessions, at least
> +				   those that can be specified uniquely with
> +				   vcnumbers */
>  	char sessid[4];		/* unique token id for this session */
>  	/* (returned on Negotiate */
>  	int capabilities; /* allow selective disabling of caps by smb sess */
> @@ -210,6 +213,7 @@ struct cifsSesInfo {
>  	unsigned overrideSecFlg;  /* if non-zero override global sec flags */
>  	__u16 ipc_tid;		/* special tid for connection to IPC share */
>  	__u16 flags;
> +	__u16 vcnum;
>  	char *serverOS;		/* name of operating system underlying server */
>  	char *serverNOS;	/* name of network operating system of server */
>  	char *serverDomain;	/* security realm of server */
> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> index 552642a..b0f4e56 100644
> --- a/fs/cifs/cifssmb.c
> +++ b/fs/cifs/cifssmb.c
> @@ -528,14 +528,15 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
>  		server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
>  		server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
>  				(__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
> +		server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
>  		GETU32(server->sessid) = le32_to_cpu(rsp->SessionKey);
>  		/* even though we do not use raw we might as well set this
>  		accurately, in case we ever find a need for it */
>  		if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
> -			server->maxRw = 0xFF00;
> +			server->max_rw = 0xFF00;
>  			server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
>  		} else {
> -			server->maxRw = 0;/* we do not need to use raw anyway */
> +			server->max_rw = 0;/* we do not need to use raw anyway */
>  			server->capabilities = CAP_MPX_MODE;
>  		}
>  		tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
> @@ -638,7 +639,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
>  	/* probably no need to store and check maxvcs */
>  	server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
>  			(__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
> -	server->maxRw = le32_to_cpu(pSMBr->MaxRawSize);
> +	server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
>  	cFYI(DBG2, ("Max buf = %d", ses->server->maxBuf));
>  	GETU32(ses->server->sessid) = le32_to_cpu(pSMBr->SessionKey);
>  	server->capabilities = le32_to_cpu(pSMBr->Capabilities);
> diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
> index 5f22de7..fdbfdca 100644
> --- a/fs/cifs/sess.c
> +++ b/fs/cifs/sess.c
> @@ -34,15 +34,62 @@
>  extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
>  			 unsigned char *p24);
>  
> +static __le16 get_next_vcnum(struct cifsSesInfo *ses)
> +{
> +	__u16 vcnum = 0;
> +	struct list_head *tmp;
> +	struct cifsSesInfo *tmp_ses;
> +	__u16 max_vcs = ses->server->max_vcs;
> +	__u16 i;
> +	int free_vc_found = 0;
> +
> +	if (max_vcs < 2)
> +		max_vcs = 0xFFFF;
> +

A comment about the above situation seems warranted since it's basically
a workaround for buggy servers.

> +	write_lock(&cifs_tcp_ses_lock);
> +	for(i = ses->server->srv_count - 1; i < max_vcs; i++) {
> +		if (i == 0) /* this is the only connection, use vc 0 */
> +			break;
> +
> +		free_vc_found = 1;
> +
> +		list_for_each(tmp, &ses->server->smb_ses_list) {
> +			tmp_ses = list_entry(tmp, struct cifsSesInfo,
> +					     smb_ses_list);
> +			if (tmp_ses->vcnum == i) {
> +				free_vc_found = 0;
> +				break; /* found duplicate, try next vcnum */
> +			}
> +		}
> +		if (free_vc_found)
> +			break; /* we found a vcnumber that will work - use it */
> +	}
> +
> +	if (free_vc_found == 0)
> +		vcnum = 0; /* for most common case, ie if one smb session, use
> +			      vc zero.  Also for case when no free vcnum, zero
> +			      is safest to send (some clients only send zero) */
> +	else
> +		vcnum = i;
> +	ses->vcnum = vcnum;
> +	write_unlock(&cifs_tcp_ses_lock);
> +
> +	return le16_to_cpu(vcnum);
> +}
> +
>  static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)
>  {
>  	__u32 capabilities = 0;
>  
>  	/* init fields common to all four types of SessSetup */
> -	/* note that header is initialized to zero in header_assemble */
> +	/* Note that offsets for first seven fields in req struct are same  */
> +	/*	in CIFS Specs so does not matter which of 3 forms of struct */
> +	/*	that we use in next few lines                               */
> +	/* Note that header is initialized to zero in header_assemble */
>  	pSMB->req.AndXCommand = 0xFF;
>  	pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
>  	pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
> +	pSMB->req.VcNumber = get_next_vcnum(ses);
>  
>  	/* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
>  
> @@ -71,7 +118,6 @@ static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)
>  	if (ses->capabilities & CAP_UNIX)
>  		capabilities |= CAP_UNIX;
>  
> -	/* BB check whether to init vcnum BB */
>  	return capabilities;
>  }
>  

So what happens at reconnect? Shouldn't we "reset" all the vcnums?
Shouldn't the first session setup on the reconnected socket use vcnum
0? I seem to recall reading that vcnum of 0 has some significance for
the server...

Other than that, the patch looks reasonable to me.
-- 
Jeff Layton <jlayton at redhat.com>


More information about the linux-cifs-client mailing list