smb3: add debug messages for closing unmatched open
[linux-2.6-microblaze.git] / fs / cifs / smb2misc.c
index e311f58..289915f 100644 (file)
@@ -249,16 +249,10 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr)
                 * of junk. Other servers match RFC1001 len to actual
                 * SMB2/SMB3 frame length (header + smb2 response specific data)
                 * Some windows servers also pad up to 8 bytes when compounding.
-                * If pad is longer than eight bytes, log the server behavior
-                * (once), since may indicate a problem but allow it and continue
-                * since the frame is parseable.
                 */
-               if (clc_len < len) {
-                       pr_warn_once(
-                            "srv rsp padded more than expected. Length %d not %d for cmd:%d mid:%llu\n",
-                            len, clc_len, command, mid);
+               if (clc_len < len)
                        return 0;
-               }
+
                pr_warn_once(
                        "srv rsp too short, len %d not %d. cmd:%d mid:%llu\n",
                        len, clc_len, command, mid);
@@ -735,45 +729,98 @@ smb2_cancelled_close_fid(struct work_struct *work)
 {
        struct close_cancelled_open *cancelled = container_of(work,
                                        struct close_cancelled_open, work);
+       struct cifs_tcon *tcon = cancelled->tcon;
+       int rc;
+
+       if (cancelled->mid)
+               cifs_tcon_dbg(VFS, "Close unmatched open for MID:%llx\n",
+                             cancelled->mid);
+       else
+               cifs_tcon_dbg(VFS, "Close interrupted close\n");
 
-       cifs_dbg(VFS, "Close unmatched open\n");
+       rc = SMB2_close(0, tcon, cancelled->fid.persistent_fid,
+                       cancelled->fid.volatile_fid);
+       if (rc)
+               cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc);
 
-       SMB2_close(0, cancelled->tcon, cancelled->fid.persistent_fid,
-                  cancelled->fid.volatile_fid);
-       cifs_put_tcon(cancelled->tcon);
+       cifs_put_tcon(tcon);
        kfree(cancelled);
 }
 
+/*
+ * Caller should already has an extra reference to @tcon
+ * This function is used to queue work to close a handle to prevent leaks
+ * on the server.
+ * We handle two cases. If an open was interrupted after we sent the
+ * SMB2_CREATE to the server but before we processed the reply, and second
+ * if a close was interrupted before we sent the SMB2_CLOSE to the server.
+ */
+static int
+__smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid,
+                           __u64 persistent_fid, __u64 volatile_fid)
+{
+       struct close_cancelled_open *cancelled;
+
+       cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
+       if (!cancelled)
+               return -ENOMEM;
+
+       cancelled->fid.persistent_fid = persistent_fid;
+       cancelled->fid.volatile_fid = volatile_fid;
+       cancelled->tcon = tcon;
+       cancelled->cmd = cmd;
+       cancelled->mid = mid;
+       INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
+       WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false);
+
+       return 0;
+}
+
+int
+smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
+                           __u64 volatile_fid)
+{
+       int rc;
+
+       cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
+       spin_lock(&cifs_tcp_ses_lock);
+       tcon->tc_count++;
+       spin_unlock(&cifs_tcp_ses_lock);
+
+       rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0,
+                                        persistent_fid, volatile_fid);
+       if (rc)
+               cifs_put_tcon(tcon);
+
+       return rc;
+}
+
 int
 smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
 {
        struct smb2_sync_hdr *sync_hdr = (struct smb2_sync_hdr *)buffer;
        struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer;
        struct cifs_tcon *tcon;
-       struct close_cancelled_open *cancelled;
+       int rc;
 
        if (sync_hdr->Command != SMB2_CREATE ||
            sync_hdr->Status != STATUS_SUCCESS)
                return 0;
 
-       cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
-       if (!cancelled)
-               return -ENOMEM;
-
        tcon = smb2_find_smb_tcon(server, sync_hdr->SessionId,
                                  sync_hdr->TreeId);
-       if (!tcon) {
-               kfree(cancelled);
+       if (!tcon)
                return -ENOENT;
-       }
 
-       cancelled->fid.persistent_fid = rsp->PersistentFileId;
-       cancelled->fid.volatile_fid = rsp->VolatileFileId;
-       cancelled->tcon = tcon;
-       INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
-       queue_work(cifsiod_wq, &cancelled->work);
+       rc = __smb2_handle_cancelled_cmd(tcon,
+                                        le16_to_cpu(sync_hdr->Command),
+                                        le64_to_cpu(sync_hdr->MessageId),
+                                        rsp->PersistentFileId,
+                                        rsp->VolatileFileId);
+       if (rc)
+               cifs_put_tcon(tcon);
 
-       return 0;
+       return rc;
 }
 
 /**