cifs: Send a logoff request before removing a smb session
authorShirish Pargaonkar <shirishpargaonkar@gmail.com>
Sat, 12 Oct 2013 15:06:03 +0000 (10:06 -0500)
committerSteve French <smfrench@gmail.com>
Sat, 2 Nov 2013 17:52:35 +0000 (12:52 -0500)
Send a smb session logoff request before removing smb session off of the list.
On a signed smb session, remvoing a session off of the list before sending
a logoff request results in server returning an error for lack of
smb signature.

Never seen an error during smb logoff, so as per MS-SMB2 3.2.5.1,
not sure how an error during logoff should be retried. So for now,
if a server returns an error to a logoff request, log the error and
remove the session off of the list.

Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <smfrench@gmail.com>
fs/cifs/connect.c
fs/cifs/smb2transport.c
fs/cifs/transport.c

index a279ffc0bc29577ed447319467b2e92248f09149..62a55147400a50adcf4c7a37756add3cc5410325 100644 (file)
@@ -2242,6 +2242,8 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
 
        spin_lock(&cifs_tcp_ses_lock);
        list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+               if (ses->status == CifsExiting)
+                       continue;
                if (!match_session(ses, vol))
                        continue;
                ++ses->ses_count;
@@ -2255,24 +2257,37 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
 static void
 cifs_put_smb_ses(struct cifs_ses *ses)
 {
-       unsigned int xid;
+       unsigned int rc, xid;
        struct TCP_Server_Info *server = ses->server;
 
        cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count);
+
        spin_lock(&cifs_tcp_ses_lock);
+       if (ses->status == CifsExiting) {
+               spin_unlock(&cifs_tcp_ses_lock);
+               return;
+       }
        if (--ses->ses_count > 0) {
                spin_unlock(&cifs_tcp_ses_lock);
                return;
        }
-
-       list_del_init(&ses->smb_ses_list);
+       if (ses->status == CifsGood)
+               ses->status = CifsExiting;
        spin_unlock(&cifs_tcp_ses_lock);
 
-       if (ses->status == CifsGood && server->ops->logoff) {
+       if (ses->status == CifsExiting && server->ops->logoff) {
                xid = get_xid();
-               server->ops->logoff(xid, ses);
+               rc = server->ops->logoff(xid, ses);
+               if (rc)
+                       cifs_dbg(VFS, "%s: Session Logoff failure rc=%d\n",
+                               __func__, rc);
                _free_xid(xid);
        }
+
+       spin_lock(&cifs_tcp_ses_lock);
+       list_del_init(&ses->smb_ses_list);
+       spin_unlock(&cifs_tcp_ses_lock);
+
        sesInfoFree(ses);
        cifs_put_tcp_session(server);
 }
index c523617eade27cb8eeeedba7a7644d0b8c8815fc..59c748ce872f9bb190bf683c8fd49a38926e3b4d 100644 (file)
@@ -516,13 +516,19 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_hdr *buf,
                return -EAGAIN;
        }
 
-       if (ses->status != CifsGood) {
-               /* check if SMB2 session is bad because we are setting it up */
+       if (ses->status == CifsNew) {
                if ((buf->Command != SMB2_SESSION_SETUP) &&
                    (buf->Command != SMB2_NEGOTIATE))
                        return -EAGAIN;
                /* else ok - we are setting up session */
        }
+
+       if (ses->status == CifsExiting) {
+               if (buf->Command != SMB2_LOGOFF)
+                       return -EAGAIN;
+               /* else ok - we are shutting down the session */
+       }
+
        *mid = smb2_mid_entry_alloc(buf, ses->server);
        if (*mid == NULL)
                return -ENOMEM;
index 0ee6d249ef6f7fe2d008067091bcc56f6145a5cd..b375709528467b5a1a74e078fae2aeb7813e1a1e 100644 (file)
@@ -431,13 +431,20 @@ static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
                return -EAGAIN;
        }
 
-       if (ses->status != CifsGood) {
-               /* check if SMB session is bad because we are setting it up */
+       if (ses->status == CifsNew) {
                if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
                        (in_buf->Command != SMB_COM_NEGOTIATE))
                        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)
+                       return -EAGAIN;
+               /* else ok - we are shutting down session */
+       }
+
        *ppmidQ = AllocMidQEntry(in_buf, ses->server);
        if (*ppmidQ == NULL)
                return -ENOMEM;