aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorSachin Prabhu <sprabhu@redhat.com>2017-03-03 18:41:38 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-05-08 01:47:55 -0400
commitd8fd99d4721d4a33c928ee0de8370b294a2df150 (patch)
tree2da82ae539ed59a667cdf239848c399d28581cba /fs
parent00cca9768ebe1ac4ac16366662dd9087b6e5f4e7 (diff)
Handle mismatched open calls
commit 38bd49064a1ecb67baad33598e3d824448ab11ec upstream. A signal can interrupt a SendReceive call which result in incoming responses to the call being ignored. This is a problem for calls such as open which results in the successful response being ignored. This results in an open file resource on the server. The patch looks into responses which were cancelled after being sent and in case of successful open closes the open fids. For this patch, the check is only done in SendReceive2() RH-bz: 1403319 Signed-off-by: Sachin Prabhu <sprabhu@redhat.com> Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com> Acked-by: Sachin Prabhu <sprabhu@redhat.com> Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/cifsglob.h11
-rw-r--r--fs/cifs/cifssmb.c4
-rw-r--r--fs/cifs/connect.c13
-rw-r--r--fs/cifs/smb2misc.c44
-rw-r--r--fs/cifs/smb2ops.c4
-rw-r--r--fs/cifs/smb2proto.h7
-rw-r--r--fs/cifs/smb2transport.c58
-rw-r--r--fs/cifs/transport.c2
8 files changed, 135 insertions, 8 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 94661cf77ae8..b3830f7ab260 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -241,6 +241,7 @@ struct smb_version_operations {
241 /* verify the message */ 241 /* verify the message */
242 int (*check_message)(char *, unsigned int, struct TCP_Server_Info *); 242 int (*check_message)(char *, unsigned int, struct TCP_Server_Info *);
243 bool (*is_oplock_break)(char *, struct TCP_Server_Info *); 243 bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
244 int (*handle_cancelled_mid)(char *, struct TCP_Server_Info *);
244 void (*downgrade_oplock)(struct TCP_Server_Info *, 245 void (*downgrade_oplock)(struct TCP_Server_Info *,
245 struct cifsInodeInfo *, bool); 246 struct cifsInodeInfo *, bool);
246 /* process transaction2 response */ 247 /* process transaction2 response */
@@ -1314,12 +1315,19 @@ struct mid_q_entry {
1314 void *callback_data; /* general purpose pointer for callback */ 1315 void *callback_data; /* general purpose pointer for callback */
1315 void *resp_buf; /* pointer to received SMB header */ 1316 void *resp_buf; /* pointer to received SMB header */
1316 int mid_state; /* wish this were enum but can not pass to wait_event */ 1317 int mid_state; /* wish this were enum but can not pass to wait_event */
1318 unsigned int mid_flags;
1317 __le16 command; /* smb command code */ 1319 __le16 command; /* smb command code */
1318 bool large_buf:1; /* if valid response, is pointer to large buf */ 1320 bool large_buf:1; /* if valid response, is pointer to large buf */
1319 bool multiRsp:1; /* multiple trans2 responses for one request */ 1321 bool multiRsp:1; /* multiple trans2 responses for one request */
1320 bool multiEnd:1; /* both received */ 1322 bool multiEnd:1; /* both received */
1321}; 1323};
1322 1324
1325struct close_cancelled_open {
1326 struct cifs_fid fid;
1327 struct cifs_tcon *tcon;
1328 struct work_struct work;
1329};
1330
1323/* Make code in transport.c a little cleaner by moving 1331/* Make code in transport.c a little cleaner by moving
1324 update of optional stats into function below */ 1332 update of optional stats into function below */
1325#ifdef CONFIG_CIFS_STATS2 1333#ifdef CONFIG_CIFS_STATS2
@@ -1451,6 +1459,9 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param,
1451#define MID_RESPONSE_MALFORMED 0x10 1459#define MID_RESPONSE_MALFORMED 0x10
1452#define MID_SHUTDOWN 0x20 1460#define MID_SHUTDOWN 0x20
1453 1461
1462/* Flags */
1463#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */
1464
1454/* Types of response buffer returned from SendReceive2 */ 1465/* Types of response buffer returned from SendReceive2 */
1455#define CIFS_NO_BUFFER 0 /* Response buffer not returned */ 1466#define CIFS_NO_BUFFER 0 /* Response buffer not returned */
1456#define CIFS_SMALL_BUFFER 1 1467#define CIFS_SMALL_BUFFER 1
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index e3fed9249a04..586fdac05ec2 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -1423,6 +1423,8 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
1423 1423
1424 length = discard_remaining_data(server); 1424 length = discard_remaining_data(server);
1425 dequeue_mid(mid, rdata->result); 1425 dequeue_mid(mid, rdata->result);
1426 mid->resp_buf = server->smallbuf;
1427 server->smallbuf = NULL;
1426 return length; 1428 return length;
1427} 1429}
1428 1430
@@ -1534,6 +1536,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
1534 return cifs_readv_discard(server, mid); 1536 return cifs_readv_discard(server, mid);
1535 1537
1536 dequeue_mid(mid, false); 1538 dequeue_mid(mid, false);
1539 mid->resp_buf = server->smallbuf;
1540 server->smallbuf = NULL;
1537 return length; 1541 return length;
1538} 1542}
1539 1543
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 893be0722643..b8015de88e8c 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -882,10 +882,19 @@ cifs_demultiplex_thread(void *p)
882 882
883 server->lstrp = jiffies; 883 server->lstrp = jiffies;
884 if (mid_entry != NULL) { 884 if (mid_entry != NULL) {
885 if ((mid_entry->mid_flags & MID_WAIT_CANCELLED) &&
886 mid_entry->mid_state == MID_RESPONSE_RECEIVED &&
887 server->ops->handle_cancelled_mid)
888 server->ops->handle_cancelled_mid(
889 mid_entry->resp_buf,
890 server);
891
885 if (!mid_entry->multiRsp || mid_entry->multiEnd) 892 if (!mid_entry->multiRsp || mid_entry->multiEnd)
886 mid_entry->callback(mid_entry); 893 mid_entry->callback(mid_entry);
887 } else if (!server->ops->is_oplock_break || 894 } else if (server->ops->is_oplock_break &&
888 !server->ops->is_oplock_break(buf, server)) { 895 server->ops->is_oplock_break(buf, server)) {
896 cifs_dbg(FYI, "Received oplock break\n");
897 } else {
889 cifs_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n", 898 cifs_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n",
890 atomic_read(&midCount)); 899 atomic_read(&midCount));
891 cifs_dump_mem("Received Data is: ", buf, 900 cifs_dump_mem("Received Data is: ", buf,
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 3d383489b9cf..97307808ae42 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -654,3 +654,47 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
654 cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n"); 654 cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n");
655 return false; 655 return false;
656} 656}
657
658void
659smb2_cancelled_close_fid(struct work_struct *work)
660{
661 struct close_cancelled_open *cancelled = container_of(work,
662 struct close_cancelled_open, work);
663
664 cifs_dbg(VFS, "Close unmatched open\n");
665
666 SMB2_close(0, cancelled->tcon, cancelled->fid.persistent_fid,
667 cancelled->fid.volatile_fid);
668 cifs_put_tcon(cancelled->tcon);
669 kfree(cancelled);
670}
671
672int
673smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
674{
675 struct smb2_hdr *hdr = (struct smb2_hdr *)buffer;
676 struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer;
677 struct cifs_tcon *tcon;
678 struct close_cancelled_open *cancelled;
679
680 if (hdr->Command != SMB2_CREATE || hdr->Status != STATUS_SUCCESS)
681 return 0;
682
683 cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
684 if (!cancelled)
685 return -ENOMEM;
686
687 tcon = smb2_find_smb_tcon(server, hdr->SessionId, hdr->TreeId);
688 if (!tcon) {
689 kfree(cancelled);
690 return -ENOENT;
691 }
692
693 cancelled->fid.persistent_fid = rsp->PersistentFileId;
694 cancelled->fid.volatile_fid = rsp->VolatileFileId;
695 cancelled->tcon = tcon;
696 INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
697 queue_work(cifsiod_wq, &cancelled->work);
698
699 return 0;
700}
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 5d456ebb3813..007abf7195af 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -1565,6 +1565,7 @@ struct smb_version_operations smb20_operations = {
1565 .clear_stats = smb2_clear_stats, 1565 .clear_stats = smb2_clear_stats,
1566 .print_stats = smb2_print_stats, 1566 .print_stats = smb2_print_stats,
1567 .is_oplock_break = smb2_is_valid_oplock_break, 1567 .is_oplock_break = smb2_is_valid_oplock_break,
1568 .handle_cancelled_mid = smb2_handle_cancelled_mid,
1568 .downgrade_oplock = smb2_downgrade_oplock, 1569 .downgrade_oplock = smb2_downgrade_oplock,
1569 .need_neg = smb2_need_neg, 1570 .need_neg = smb2_need_neg,
1570 .negotiate = smb2_negotiate, 1571 .negotiate = smb2_negotiate,
@@ -1645,6 +1646,7 @@ struct smb_version_operations smb21_operations = {
1645 .clear_stats = smb2_clear_stats, 1646 .clear_stats = smb2_clear_stats,
1646 .print_stats = smb2_print_stats, 1647 .print_stats = smb2_print_stats,
1647 .is_oplock_break = smb2_is_valid_oplock_break, 1648 .is_oplock_break = smb2_is_valid_oplock_break,
1649 .handle_cancelled_mid = smb2_handle_cancelled_mid,
1648 .downgrade_oplock = smb2_downgrade_oplock, 1650 .downgrade_oplock = smb2_downgrade_oplock,
1649 .need_neg = smb2_need_neg, 1651 .need_neg = smb2_need_neg,
1650 .negotiate = smb2_negotiate, 1652 .negotiate = smb2_negotiate,
@@ -1727,6 +1729,7 @@ struct smb_version_operations smb30_operations = {
1727 .print_stats = smb2_print_stats, 1729 .print_stats = smb2_print_stats,
1728 .dump_share_caps = smb2_dump_share_caps, 1730 .dump_share_caps = smb2_dump_share_caps,
1729 .is_oplock_break = smb2_is_valid_oplock_break, 1731 .is_oplock_break = smb2_is_valid_oplock_break,
1732 .handle_cancelled_mid = smb2_handle_cancelled_mid,
1730 .downgrade_oplock = smb2_downgrade_oplock, 1733 .downgrade_oplock = smb2_downgrade_oplock,
1731 .need_neg = smb2_need_neg, 1734 .need_neg = smb2_need_neg,
1732 .negotiate = smb2_negotiate, 1735 .negotiate = smb2_negotiate,
@@ -1815,6 +1818,7 @@ struct smb_version_operations smb311_operations = {
1815 .print_stats = smb2_print_stats, 1818 .print_stats = smb2_print_stats,
1816 .dump_share_caps = smb2_dump_share_caps, 1819 .dump_share_caps = smb2_dump_share_caps,
1817 .is_oplock_break = smb2_is_valid_oplock_break, 1820 .is_oplock_break = smb2_is_valid_oplock_break,
1821 .handle_cancelled_mid = smb2_handle_cancelled_mid,
1818 .downgrade_oplock = smb2_downgrade_oplock, 1822 .downgrade_oplock = smb2_downgrade_oplock,
1819 .need_neg = smb2_need_neg, 1823 .need_neg = smb2_need_neg,
1820 .negotiate = smb2_negotiate, 1824 .negotiate = smb2_negotiate,
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index f2d511a6971b..04ef6e914597 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -48,6 +48,10 @@ extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses,
48 struct smb_rqst *rqst); 48 struct smb_rqst *rqst);
49extern struct mid_q_entry *smb2_setup_async_request( 49extern struct mid_q_entry *smb2_setup_async_request(
50 struct TCP_Server_Info *server, struct smb_rqst *rqst); 50 struct TCP_Server_Info *server, struct smb_rqst *rqst);
51extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server,
52 __u64 ses_id);
53extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server,
54 __u64 ses_id, __u32 tid);
51extern int smb2_calc_signature(struct smb_rqst *rqst, 55extern int smb2_calc_signature(struct smb_rqst *rqst,
52 struct TCP_Server_Info *server); 56 struct TCP_Server_Info *server);
53extern int smb3_calc_signature(struct smb_rqst *rqst, 57extern int smb3_calc_signature(struct smb_rqst *rqst,
@@ -158,6 +162,9 @@ extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
158extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, 162extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
159 const u64 persistent_fid, const u64 volatile_fid, 163 const u64 persistent_fid, const u64 volatile_fid,
160 const __u8 oplock_level); 164 const __u8 oplock_level);
165extern int smb2_handle_cancelled_mid(char *buffer,
166 struct TCP_Server_Info *server);
167void smb2_cancelled_close_fid(struct work_struct *work);
161extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, 168extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
162 u64 persistent_file_id, u64 volatile_file_id, 169 u64 persistent_file_id, u64 volatile_file_id,
163 struct kstatfs *FSData); 170 struct kstatfs *FSData);
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
index bc9a7b634643..390b0d0198f8 100644
--- a/fs/cifs/smb2transport.c
+++ b/fs/cifs/smb2transport.c
@@ -115,22 +115,68 @@ smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
115} 115}
116 116
117static struct cifs_ses * 117static struct cifs_ses *
118smb2_find_smb_ses(struct smb2_hdr *smb2hdr, struct TCP_Server_Info *server) 118smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
119{ 119{
120 struct cifs_ses *ses; 120 struct cifs_ses *ses;
121 121
122 spin_lock(&cifs_tcp_ses_lock);
123 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { 122 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
124 if (ses->Suid != smb2hdr->SessionId) 123 if (ses->Suid != ses_id)
125 continue; 124 continue;
126 spin_unlock(&cifs_tcp_ses_lock);
127 return ses; 125 return ses;
128 } 126 }
127
128 return NULL;
129}
130
131struct cifs_ses *
132smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id)
133{
134 struct cifs_ses *ses;
135
136 spin_lock(&cifs_tcp_ses_lock);
137 ses = smb2_find_smb_ses_unlocked(server, ses_id);
129 spin_unlock(&cifs_tcp_ses_lock); 138 spin_unlock(&cifs_tcp_ses_lock);
130 139
140 return ses;
141}
142
143static struct cifs_tcon *
144smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid)
145{
146 struct cifs_tcon *tcon;
147
148 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
149 if (tcon->tid != tid)
150 continue;
151 ++tcon->tc_count;
152 return tcon;
153 }
154
131 return NULL; 155 return NULL;
132} 156}
133 157
158/*
159 * Obtain tcon corresponding to the tid in the given
160 * cifs_ses
161 */
162
163struct cifs_tcon *
164smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid)
165{
166 struct cifs_ses *ses;
167 struct cifs_tcon *tcon;
168
169 spin_lock(&cifs_tcp_ses_lock);
170 ses = smb2_find_smb_ses_unlocked(server, ses_id);
171 if (!ses) {
172 spin_unlock(&cifs_tcp_ses_lock);
173 return NULL;
174 }
175 tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid);
176 spin_unlock(&cifs_tcp_ses_lock);
177
178 return tcon;
179}
134 180
135int 181int
136smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) 182smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
@@ -142,7 +188,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
142 struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base; 188 struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
143 struct cifs_ses *ses; 189 struct cifs_ses *ses;
144 190
145 ses = smb2_find_smb_ses(smb2_pdu, server); 191 ses = smb2_find_smb_ses(server, smb2_pdu->SessionId);
146 if (!ses) { 192 if (!ses) {
147 cifs_dbg(VFS, "%s: Could not find session\n", __func__); 193 cifs_dbg(VFS, "%s: Could not find session\n", __func__);
148 return 0; 194 return 0;
@@ -359,7 +405,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
359 struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base; 405 struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
360 struct cifs_ses *ses; 406 struct cifs_ses *ses;
361 407
362 ses = smb2_find_smb_ses(smb2_pdu, server); 408 ses = smb2_find_smb_ses(server, smb2_pdu->SessionId);
363 if (!ses) { 409 if (!ses) {
364 cifs_dbg(VFS, "%s: Could not find session\n", __func__); 410 cifs_dbg(VFS, "%s: Could not find session\n", __func__);
365 return 0; 411 return 0;
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 206a597b2293..cc26d4138d70 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -727,9 +727,11 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
727 727
728 rc = wait_for_response(ses->server, midQ); 728 rc = wait_for_response(ses->server, midQ);
729 if (rc != 0) { 729 if (rc != 0) {
730 cifs_dbg(FYI, "Cancelling wait for mid %llu\n", midQ->mid);
730 send_cancel(ses->server, buf, midQ); 731 send_cancel(ses->server, buf, midQ);
731 spin_lock(&GlobalMid_Lock); 732 spin_lock(&GlobalMid_Lock);
732 if (midQ->mid_state == MID_REQUEST_SUBMITTED) { 733 if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
734 midQ->mid_flags |= MID_WAIT_CANCELLED;
733 midQ->callback = DeleteMidQEntry; 735 midQ->callback = DeleteMidQEntry;
734 spin_unlock(&GlobalMid_Lock); 736 spin_unlock(&GlobalMid_Lock);
735 cifs_small_buf_release(buf); 737 cifs_small_buf_release(buf);