[CIFS] Ensure that cifs multiplex ids do not collide.

Signed-off-by: Steve French (sfrench@us.ibm.com)
This commit is contained in:
Steve French 2005-08-17 12:38:22 -07:00
parent a59c658607
commit 1982c344f1
6 changed files with 113 additions and 29 deletions

View file

@ -147,6 +147,7 @@ struct TCP_Server_Info {
/* (returned on Negotiate */
int capabilities; /* allow selective disabling of caps by smb sess */
__u16 timeZone;
__u16 CurrentMid; /* multiplex id - rotating counter */
char cryptKey[CIFS_CRYPTO_KEY_SIZE];
char workstation_RFC1001_name[16]; /* 16th byte is always zero */
__u32 sequence_number; /* needed for CIFS PDU signature */

View file

@ -1961,18 +1961,17 @@ struct data_blob {
perhaps add a CreateDevice - to create Pipes and other special .inodes
Also note POSIX open flags
2) Close - to return the last write time to do cache across close more safely
3) PosixQFSInfo - to return statfs info
4) FindFirst return unique inode number - what about resume key, two forms short (matches readdir) and full (enough info to cache inodes)
5) Mkdir - set mode
3) FindFirst return unique inode number - what about resume key, two
forms short (matches readdir) and full (enough info to cache inodes)
4) Mkdir - set mode
And under consideration:
6) FindClose2 (return nanosecond timestamp ??)
7) Use nanosecond timestamps throughout all time fields if
5) FindClose2 (return nanosecond timestamp ??)
6) Use nanosecond timestamps throughout all time fields if
corresponding attribute flag is set
8) sendfile - handle based copy
9) Direct i/o
10) "POSIX ACL" support
11) Misc fcntls?
7) sendfile - handle based copy
8) Direct i/o
9) Misc fcntls?
what about fixing 64 bit alignment
@ -2028,7 +2027,7 @@ struct data_blob {
*/
/* xsymlink is a symlink format that can be used
/* xsymlink is a symlink format (used by MacOS) that can be used
to save symlink info in a regular file when
mounted to operating systems that do not
support the cifs Unix extensions or EAs (for xattr

View file

@ -61,9 +61,9 @@ extern int decode_negTokenInit(unsigned char *security_blob, int length,
extern int cifs_inet_pton(int, char * source, void *dst);
extern int map_smb_to_linux_error(struct smb_hdr *smb);
extern void header_assemble(struct smb_hdr *, char /* command */ ,
const struct cifsTconInfo *, int /* specifies length
of fixed section (word count) in two byte units */
);
const struct cifsTconInfo *, int /* length of
fixed section (word count) in two byte units */);
extern __u16 GetNextMid(struct TCP_Server_Info *server);
extern struct oplock_q_entry * AllocOplockQEntry(struct inode *, u16,
struct cifsTconInfo *);
extern void DeleteOplockQEntry(struct oplock_q_entry *);

View file

@ -330,7 +330,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
(void **) &pSMB, (void **) &pSMBr);
if (rc)
return rc;
pSMB->hdr.Mid = GetNextMid(server);
pSMB->hdr.Flags2 |= SMBFLG2_UNICODE;
if (extended_security)
pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
@ -415,15 +415,14 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
if(server->secMode & SECMODE_SIGN_REQUIRED)
cERROR(1,
("Server requires /proc/fs/cifs/PacketSigningEnabled"));
server->secMode &= ~(SECMODE_SIGN_ENABLED |
SECMODE_SIGN_REQUIRED);
server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
} else if(sign_CIFS_PDUs == 1) {
if((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
server->secMode &= ~(SECMODE_SIGN_ENABLED |
SECMODE_SIGN_REQUIRED);
server->secMode &= ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
}
}
cifs_buf_release(pSMB);
return rc;
}
@ -519,6 +518,8 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
smb_buffer_response = (struct smb_hdr *)pSMB; /* BB removeme BB */
if(ses->server) {
pSMB->hdr.Mid = GetNextMid(ses->server);
if(ses->server->secMode &
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
@ -2519,11 +2520,12 @@ findFirstRetry:
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {/* BB add logic to retry regular search if Unix search
rejected unexpectedly by server */
if (rc) {/* BB add logic to retry regular search if Unix search rejected unexpectedly by server */
/* BB Add code to handle unsupported level rc */
cFYI(1, ("Error in FindFirst = %d", rc));
cifs_buf_release(pSMB);
if (pSMB)
cifs_buf_release(pSMB);
/* BB eventually could optimize out free and realloc of buf */
/* for this case */
@ -2857,7 +2859,10 @@ getDFSRetry:
(void **) &pSMBr);
if (rc)
return rc;
/* server pointer checked in called function,
but should never be null here anyway */
pSMB->hdr.Mid = GetNextMid(ses->server);
pSMB->hdr.Tid = ses->ipc_tid;
pSMB->hdr.Uid = ses->Suid;
if (ses->capabilities & CAP_STATUS32) {

View file

@ -1857,6 +1857,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
NULL /* no tCon exists yet */ , 13 /* wct */ );
smb_buffer->Mid = GetNextMid(ses->server);
pSMB->req_no_secext.AndXCommand = 0xFF;
pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
@ -2132,6 +2133,8 @@ CIFSSpnegoSessSetup(unsigned int xid, struct cifsSesInfo *ses,
/* send SMBsessionSetup here */
header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
NULL /* no tCon exists yet */ , 12 /* wct */ );
smb_buffer->Mid = GetNextMid(ses->server);
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
pSMB->req.AndXCommand = 0xFF;
pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
@ -2398,6 +2401,8 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
/* send SMBsessionSetup here */
header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
NULL /* no tCon exists yet */ , 12 /* wct */ );
smb_buffer->Mid = GetNextMid(ses->server);
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
@ -2740,6 +2745,8 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
/* send SMBsessionSetup here */
header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
NULL /* no tCon exists yet */ , 12 /* wct */ );
smb_buffer->Mid = GetNextMid(ses->server);
pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
pSMB->req.AndXCommand = 0xFF;
@ -3111,6 +3118,8 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
NULL /*no tid */ , 4 /*wct */ );
smb_buffer->Mid = GetNextMid(ses->server);
smb_buffer->Uid = ses->Suid;
pSMB = (TCONX_REQ *) smb_buffer;
pSMBr = (TCONX_RSP *) smb_buffer_response;

View file

@ -34,8 +34,6 @@ extern mempool_t *cifs_sm_req_poolp;
extern mempool_t *cifs_req_poolp;
extern struct task_struct * oplockThread;
static __u16 GlobalMid; /* multiplex id - rotating counter */
/* The xid serves as a useful identifier for each incoming vfs request,
in a similar way to the mid which is useful to track each sent smb,
and CurrentXid can also provide a running counter (although it
@ -51,6 +49,8 @@ _GetXid(void)
GlobalTotalActiveXid++;
if (GlobalTotalActiveXid > GlobalMaxActiveXid)
GlobalMaxActiveXid = GlobalTotalActiveXid; /* keep high water mark for number of simultaneous vfs ops in our filesystem */
if(GlobalTotalActiveXid > 65000)
cFYI(1,("warning: more than 65000 requests active"));
xid = GlobalCurrentXid++;
spin_unlock(&GlobalMid_Lock);
return xid;
@ -218,6 +218,76 @@ cifs_small_buf_release(void *buf_to_free)
return;
}
/*
Find a free multiplex id (SMB mid). Otherwise there could be
mid collisions which might cause problems, demultiplexing the
wrong response to this request. Multiplex ids could collide if
one of a series requests takes much longer than the others, or
if a very large number of long lived requests (byte range
locks or FindNotify requests) are pending. No more than
64K-1 requests can be outstanding at one time. If no
mids are available, return zero. A future optimization
could make the combination of mids and uid the key we use
to demultiplex on (rather than mid alone).
In addition to the above check, the cifs demultiplex
code already used the command code as a secondary
check of the frame and if signing is negotiated the
response would be discarded if the mid were the same
but the signature was wrong. Since the mid is not put in the
pending queue until later (when it is about to be dispatched)
we do have to limit the number of outstanding requests
to somewhat less than 64K-1 although it is hard to imagine
so many threads being in the vfs at one time.
*/
__u16 GetNextMid(struct TCP_Server_Info *server)
{
__u16 mid = 0;
__u16 last_mid;
int collision;
if(server == NULL)
return mid;
spin_lock(&GlobalMid_Lock);
last_mid = server->CurrentMid; /* we do not want to loop forever */
server->CurrentMid++;
/* This nested loop looks more expensive than it is.
In practice the list of pending requests is short,
fewer than 50, and the mids are likely to be unique
on the first pass through the loop unless some request
takes longer than the 64 thousand requests before it
(and it would also have to have been a request that
did not time out) */
while(server->CurrentMid != last_mid) {
struct list_head *tmp;
struct mid_q_entry *mid_entry;
collision = 0;
if(server->CurrentMid == 0)
server->CurrentMid++;
list_for_each(tmp, &server->pending_mid_q) {
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
if ((mid_entry->mid == server->CurrentMid) &&
(mid_entry->midState == MID_REQUEST_SUBMITTED)) {
/* This mid is in use, try a different one */
collision = 1;
break;
}
}
if(collision == 0) {
mid = server->CurrentMid;
break;
}
server->CurrentMid++;
}
spin_unlock(&GlobalMid_Lock);
return mid;
}
/* NB: MID can not be set if treeCon not passed in, in that
case it is responsbility of caller to set the mid */
void
header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
const struct cifsTconInfo *treeCon, int word_count
@ -233,7 +303,8 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
(2 * word_count) + sizeof (struct smb_hdr) -
4 /* RFC 1001 length field does not count */ +
2 /* for bcc field itself */ ;
/* Note that this is the only network field that has to be converted to big endian and it is done just before we send it */
/* Note that this is the only network field that has to be converted
to big endian and it is done just before we send it */
buffer->Protocol[0] = 0xFF;
buffer->Protocol[1] = 'S';
@ -245,8 +316,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
buffer->Pid = cpu_to_le16((__u16)current->tgid);
buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16));
spin_lock(&GlobalMid_Lock);
GlobalMid++;
buffer->Mid = GlobalMid;
spin_unlock(&GlobalMid_Lock);
if (treeCon) {
buffer->Tid = treeCon->tid;
@ -256,8 +325,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
if (treeCon->ses->capabilities & CAP_STATUS32) {
buffer->Flags2 |= SMBFLG2_ERR_STATUS;
}
buffer->Uid = treeCon->ses->Suid; /* always in LE format */
/* Uid is not converted */
buffer->Uid = treeCon->ses->Suid;
buffer->Mid = GetNextMid(treeCon->ses->server);
if(multiuser_mount != 0) {
/* For the multiuser case, there are few obvious technically */
/* possible mechanisms to match the local linux user (uid) */