]> err.no Git - linux-2.6/commitdiff
[CIFS] Ensure that cifs multiplex ids do not collide.
authorSteve French <sfrench@us.ibm.com>
Wed, 17 Aug 2005 19:38:22 +0000 (12:38 -0700)
committerSteve French <sfrench@us.ibm.com>
Wed, 17 Aug 2005 19:38:22 +0000 (12:38 -0700)
Signed-off-by: Steve French (sfrench@us.ibm.com)
fs/cifs/cifsglob.h
fs/cifs/cifspdu.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/misc.c

index d3773e57acf99e75aa490f59d89bc18c9a133458..e8287f76484f13ca19c66761d4d611b48a07c8f9 100644 (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 */
index 3cef57b7a34f38d1438c319bd5856cae2322e642..49cc66825309eab46df4d0630e7730647a3417a1 100644 (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
index 66eaa6b40373166730ecc3123ef7fdc8633bdb85..b9b13e3fe79d1c4617244a41946db4a04f924acc 100644 (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 *);
index 1b073546f5d901c36cf0e40fa1216569f9221fd5..930be0927de2215e808a9ba0c67f85f9fc3f8e5d 100644 (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) {
index 36f78596c81a8a97900b36eb7c6b88486b3798fc..9e8256003f7315d01c473252abdb1f5f7e44e172 100644 (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;
index 20ae4153f791673d4137bf885db2b8b92cda8852..beeff82841696c63e0992b5ed5b9054b409486c3 100644 (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)    */