mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 14:42:24 +00:00
[CIFS] Ensure that cifs multiplex ids do not collide.
Signed-off-by: Steve French (sfrench@us.ibm.com)
This commit is contained in:
parent
a59c658607
commit
1982c344f1
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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 *);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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) */
|
||||
|
Loading…
Reference in New Issue
Block a user