aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSachin Prabhu <sprabhu@redhat.com>2017-03-03 18:41:38 -0500
committerSteve French <smfrench@gmail.com>2017-04-07 09:04:40 -0400
commit38bd49064a1ecb67baad33598e3d824448ab11ec (patch)
treedd922c0d30ddffd7e0bda9e53ddca5165c1afb84
parent269c930e6698e6afa6d4606bfd241f4497597e79 (diff)
Handle mismatched open calls
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> Cc: Stable <stable@vger.kernel.org>
-rw-r--r--fs/cifs/cifsglob.h11
-rw-r--r--fs/cifs/cifsproto.h3
-rw-r--r--fs/cifs/cifssmb.c11
-rw-r--r--fs/cifs/connect.c13
-rw-r--r--fs/cifs/smb2misc.c46
-rw-r--r--fs/cifs/smb2ops.c8
-rw-r--r--fs/cifs/smb2proto.h7
-rw-r--r--fs/cifs/smb2transport.c55
-rw-r--r--fs/cifs/transport.c2
9 files changed, 143 insertions, 13 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index d42dd3288647..c34bdb12c8e6 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -243,6 +243,7 @@ struct smb_version_operations {
243 /* verify the message */ 243 /* verify the message */
244 int (*check_message)(char *, unsigned int, struct TCP_Server_Info *); 244 int (*check_message)(char *, unsigned int, struct TCP_Server_Info *);
245 bool (*is_oplock_break)(char *, struct TCP_Server_Info *); 245 bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
246 int (*handle_cancelled_mid)(char *, struct TCP_Server_Info *);
246 void (*downgrade_oplock)(struct TCP_Server_Info *, 247 void (*downgrade_oplock)(struct TCP_Server_Info *,
247 struct cifsInodeInfo *, bool); 248 struct cifsInodeInfo *, bool);
248 /* process transaction2 response */ 249 /* process transaction2 response */
@@ -1343,6 +1344,7 @@ struct mid_q_entry {
1343 void *callback_data; /* general purpose pointer for callback */ 1344 void *callback_data; /* general purpose pointer for callback */
1344 void *resp_buf; /* pointer to received SMB header */ 1345 void *resp_buf; /* pointer to received SMB header */
1345 int mid_state; /* wish this were enum but can not pass to wait_event */ 1346 int mid_state; /* wish this were enum but can not pass to wait_event */
1347 unsigned int mid_flags;
1346 __le16 command; /* smb command code */ 1348 __le16 command; /* smb command code */
1347 bool large_buf:1; /* if valid response, is pointer to large buf */ 1349 bool large_buf:1; /* if valid response, is pointer to large buf */
1348 bool multiRsp:1; /* multiple trans2 responses for one request */ 1350 bool multiRsp:1; /* multiple trans2 responses for one request */
@@ -1350,6 +1352,12 @@ struct mid_q_entry {
1350 bool decrypted:1; /* decrypted entry */ 1352 bool decrypted:1; /* decrypted entry */
1351}; 1353};
1352 1354
1355struct close_cancelled_open {
1356 struct cifs_fid fid;
1357 struct cifs_tcon *tcon;
1358 struct work_struct work;
1359};
1360
1353/* Make code in transport.c a little cleaner by moving 1361/* Make code in transport.c a little cleaner by moving
1354 update of optional stats into function below */ 1362 update of optional stats into function below */
1355#ifdef CONFIG_CIFS_STATS2 1363#ifdef CONFIG_CIFS_STATS2
@@ -1481,6 +1489,9 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param,
1481#define MID_RESPONSE_MALFORMED 0x10 1489#define MID_RESPONSE_MALFORMED 0x10
1482#define MID_SHUTDOWN 0x20 1490#define MID_SHUTDOWN 0x20
1483 1491
1492/* Flags */
1493#define MID_WAIT_CANCELLED 1 /* Cancelled while waiting for response */
1494
1484/* Types of response buffer returned from SendReceive2 */ 1495/* Types of response buffer returned from SendReceive2 */
1485#define CIFS_NO_BUFFER 0 /* Response buffer not returned */ 1496#define CIFS_NO_BUFFER 0 /* Response buffer not returned */
1486#define CIFS_SMALL_BUFFER 1 1497#define CIFS_SMALL_BUFFER 1
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 97e5d236d265..ec5e5e514fdd 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -79,7 +79,8 @@ extern void cifs_delete_mid(struct mid_q_entry *mid);
79extern void cifs_wake_up_task(struct mid_q_entry *mid); 79extern void cifs_wake_up_task(struct mid_q_entry *mid);
80extern int cifs_handle_standard(struct TCP_Server_Info *server, 80extern int cifs_handle_standard(struct TCP_Server_Info *server,
81 struct mid_q_entry *mid); 81 struct mid_q_entry *mid);
82extern int cifs_discard_remaining_data(struct TCP_Server_Info *server); 82extern int cifs_discard_remaining_data(struct TCP_Server_Info *server,
83 char *buf);
83extern int cifs_call_async(struct TCP_Server_Info *server, 84extern int cifs_call_async(struct TCP_Server_Info *server,
84 struct smb_rqst *rqst, 85 struct smb_rqst *rqst,
85 mid_receive_t *receive, mid_callback_t *callback, 86 mid_receive_t *receive, mid_callback_t *callback,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 066950671929..967b92631807 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -1400,9 +1400,9 @@ openRetry:
1400 * current bigbuf. 1400 * current bigbuf.
1401 */ 1401 */
1402int 1402int
1403cifs_discard_remaining_data(struct TCP_Server_Info *server) 1403cifs_discard_remaining_data(struct TCP_Server_Info *server, char *buf)
1404{ 1404{
1405 unsigned int rfclen = get_rfc1002_length(server->smallbuf); 1405 unsigned int rfclen = get_rfc1002_length(buf);
1406 int remaining = rfclen + 4 - server->total_read; 1406 int remaining = rfclen + 4 - server->total_read;
1407 1407
1408 while (remaining > 0) { 1408 while (remaining > 0) {
@@ -1426,7 +1426,7 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
1426 int length; 1426 int length;
1427 struct cifs_readdata *rdata = mid->callback_data; 1427 struct cifs_readdata *rdata = mid->callback_data;
1428 1428
1429 length = cifs_discard_remaining_data(server); 1429 length = cifs_discard_remaining_data(server, mid->resp_buf);
1430 dequeue_mid(mid, rdata->result); 1430 dequeue_mid(mid, rdata->result);
1431 return length; 1431 return length;
1432} 1432}
@@ -1459,7 +1459,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
1459 1459
1460 if (server->ops->is_status_pending && 1460 if (server->ops->is_status_pending &&
1461 server->ops->is_status_pending(buf, server, 0)) { 1461 server->ops->is_status_pending(buf, server, 0)) {
1462 cifs_discard_remaining_data(server); 1462 cifs_discard_remaining_data(server, buf);
1463 return -1; 1463 return -1;
1464 } 1464 }
1465 1465
@@ -1519,6 +1519,9 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
1519 cifs_dbg(FYI, "0: iov_base=%p iov_len=%u\n", 1519 cifs_dbg(FYI, "0: iov_base=%p iov_len=%u\n",
1520 rdata->iov[0].iov_base, server->total_read); 1520 rdata->iov[0].iov_base, server->total_read);
1521 1521
1522 mid->resp_buf = server->smallbuf;
1523 server->smallbuf = NULL;
1524
1522 /* how much data is in the response? */ 1525 /* how much data is in the response? */
1523 data_len = server->ops->read_data_length(buf); 1526 data_len = server->ops->read_data_length(buf);
1524 if (data_offset + data_len > buflen) { 1527 if (data_offset + data_len > buflen) {
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 9ae695ae3ed7..0c7596cef4b8 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -904,10 +904,19 @@ cifs_demultiplex_thread(void *p)
904 904
905 server->lstrp = jiffies; 905 server->lstrp = jiffies;
906 if (mid_entry != NULL) { 906 if (mid_entry != NULL) {
907 if ((mid_entry->mid_flags & MID_WAIT_CANCELLED) &&
908 mid_entry->mid_state == MID_RESPONSE_RECEIVED &&
909 server->ops->handle_cancelled_mid)
910 server->ops->handle_cancelled_mid(
911 mid_entry->resp_buf,
912 server);
913
907 if (!mid_entry->multiRsp || mid_entry->multiEnd) 914 if (!mid_entry->multiRsp || mid_entry->multiEnd)
908 mid_entry->callback(mid_entry); 915 mid_entry->callback(mid_entry);
909 } else if (!server->ops->is_oplock_break || 916 } else if (server->ops->is_oplock_break &&
910 !server->ops->is_oplock_break(buf, server)) { 917 server->ops->is_oplock_break(buf, server)) {
918 cifs_dbg(FYI, "Received oplock break\n");
919 } else {
911 cifs_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n", 920 cifs_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n",
912 atomic_read(&midCount)); 921 atomic_read(&midCount));
913 cifs_dump_mem("Received Data is: ", buf, 922 cifs_dump_mem("Received Data is: ", buf,
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index fd516ea8b8f8..1a04b3a5beb1 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -659,3 +659,49 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
659 cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n"); 659 cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n");
660 return false; 660 return false;
661} 661}
662
663void
664smb2_cancelled_close_fid(struct work_struct *work)
665{
666 struct close_cancelled_open *cancelled = container_of(work,
667 struct close_cancelled_open, work);
668
669 cifs_dbg(VFS, "Close unmatched open\n");
670
671 SMB2_close(0, cancelled->tcon, cancelled->fid.persistent_fid,
672 cancelled->fid.volatile_fid);
673 cifs_put_tcon(cancelled->tcon);
674 kfree(cancelled);
675}
676
677int
678smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
679{
680 struct smb2_sync_hdr *sync_hdr = get_sync_hdr(buffer);
681 struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer;
682 struct cifs_tcon *tcon;
683 struct close_cancelled_open *cancelled;
684
685 if (sync_hdr->Command != SMB2_CREATE ||
686 sync_hdr->Status != STATUS_SUCCESS)
687 return 0;
688
689 cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
690 if (!cancelled)
691 return -ENOMEM;
692
693 tcon = smb2_find_smb_tcon(server, sync_hdr->SessionId,
694 sync_hdr->TreeId);
695 if (!tcon) {
696 kfree(cancelled);
697 return -ENOENT;
698 }
699
700 cancelled->fid.persistent_fid = rsp->PersistentFileId;
701 cancelled->fid.volatile_fid = rsp->VolatileFileId;
702 cancelled->tcon = tcon;
703 INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
704 queue_work(cifsiod_wq, &cancelled->work);
705
706 return 0;
707}
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 0231108d9387..b6bdf93042eb 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -2188,7 +2188,7 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid)
2188 if (rc) 2188 if (rc)
2189 goto free_pages; 2189 goto free_pages;
2190 2190
2191 rc = cifs_discard_remaining_data(server); 2191 rc = cifs_discard_remaining_data(server, buf);
2192 if (rc) 2192 if (rc)
2193 goto free_pages; 2193 goto free_pages;
2194 2194
@@ -2214,7 +2214,7 @@ free_pages:
2214 kfree(pages); 2214 kfree(pages);
2215 return rc; 2215 return rc;
2216discard_data: 2216discard_data:
2217 cifs_discard_remaining_data(server); 2217 cifs_discard_remaining_data(server, buf);
2218 goto free_pages; 2218 goto free_pages;
2219} 2219}
2220 2220
@@ -2322,6 +2322,7 @@ struct smb_version_operations smb20_operations = {
2322 .clear_stats = smb2_clear_stats, 2322 .clear_stats = smb2_clear_stats,
2323 .print_stats = smb2_print_stats, 2323 .print_stats = smb2_print_stats,
2324 .is_oplock_break = smb2_is_valid_oplock_break, 2324 .is_oplock_break = smb2_is_valid_oplock_break,
2325 .handle_cancelled_mid = smb2_handle_cancelled_mid,
2325 .downgrade_oplock = smb2_downgrade_oplock, 2326 .downgrade_oplock = smb2_downgrade_oplock,
2326 .need_neg = smb2_need_neg, 2327 .need_neg = smb2_need_neg,
2327 .negotiate = smb2_negotiate, 2328 .negotiate = smb2_negotiate,
@@ -2404,6 +2405,7 @@ struct smb_version_operations smb21_operations = {
2404 .clear_stats = smb2_clear_stats, 2405 .clear_stats = smb2_clear_stats,
2405 .print_stats = smb2_print_stats, 2406 .print_stats = smb2_print_stats,
2406 .is_oplock_break = smb2_is_valid_oplock_break, 2407 .is_oplock_break = smb2_is_valid_oplock_break,
2408 .handle_cancelled_mid = smb2_handle_cancelled_mid,
2407 .downgrade_oplock = smb2_downgrade_oplock, 2409 .downgrade_oplock = smb2_downgrade_oplock,
2408 .need_neg = smb2_need_neg, 2410 .need_neg = smb2_need_neg,
2409 .negotiate = smb2_negotiate, 2411 .negotiate = smb2_negotiate,
@@ -2488,6 +2490,7 @@ struct smb_version_operations smb30_operations = {
2488 .print_stats = smb2_print_stats, 2490 .print_stats = smb2_print_stats,
2489 .dump_share_caps = smb2_dump_share_caps, 2491 .dump_share_caps = smb2_dump_share_caps,
2490 .is_oplock_break = smb2_is_valid_oplock_break, 2492 .is_oplock_break = smb2_is_valid_oplock_break,
2493 .handle_cancelled_mid = smb2_handle_cancelled_mid,
2491 .downgrade_oplock = smb2_downgrade_oplock, 2494 .downgrade_oplock = smb2_downgrade_oplock,
2492 .need_neg = smb2_need_neg, 2495 .need_neg = smb2_need_neg,
2493 .negotiate = smb2_negotiate, 2496 .negotiate = smb2_negotiate,
@@ -2582,6 +2585,7 @@ struct smb_version_operations smb311_operations = {
2582 .print_stats = smb2_print_stats, 2585 .print_stats = smb2_print_stats,
2583 .dump_share_caps = smb2_dump_share_caps, 2586 .dump_share_caps = smb2_dump_share_caps,
2584 .is_oplock_break = smb2_is_valid_oplock_break, 2587 .is_oplock_break = smb2_is_valid_oplock_break,
2588 .handle_cancelled_mid = smb2_handle_cancelled_mid,
2585 .downgrade_oplock = smb2_downgrade_oplock, 2589 .downgrade_oplock = smb2_downgrade_oplock,
2586 .need_neg = smb2_need_neg, 2590 .need_neg = smb2_need_neg,
2587 .negotiate = smb2_negotiate, 2591 .negotiate = smb2_negotiate,
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 69e35873b1de..6853454fc871 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,
@@ -164,6 +168,9 @@ extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
164extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon, 168extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
165 const u64 persistent_fid, const u64 volatile_fid, 169 const u64 persistent_fid, const u64 volatile_fid,
166 const __u8 oplock_level); 170 const __u8 oplock_level);
171extern int smb2_handle_cancelled_mid(char *buffer,
172 struct TCP_Server_Info *server);
173void smb2_cancelled_close_fid(struct work_struct *work);
167extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon, 174extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
168 u64 persistent_file_id, u64 volatile_file_id, 175 u64 persistent_file_id, u64 volatile_file_id,
169 struct kstatfs *FSData); 176 struct kstatfs *FSData);
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
index 7c3bb1bd7eed..506b67fc93d9 100644
--- a/fs/cifs/smb2transport.c
+++ b/fs/cifs/smb2transport.c
@@ -115,23 +115,70 @@ smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
115 return 0; 115 return 0;
116} 116}
117 117
118struct cifs_ses * 118static struct cifs_ses *
119smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id) 119smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
120{ 120{
121 struct cifs_ses *ses; 121 struct cifs_ses *ses;
122 122
123 spin_lock(&cifs_tcp_ses_lock);
124 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { 123 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
125 if (ses->Suid != ses_id) 124 if (ses->Suid != ses_id)
126 continue; 125 continue;
127 spin_unlock(&cifs_tcp_ses_lock);
128 return ses; 126 return ses;
129 } 127 }
128
129 return NULL;
130}
131
132struct cifs_ses *
133smb2_find_smb_ses(struct TCP_Server_Info *server, __u64 ses_id)
134{
135 struct cifs_ses *ses;
136
137 spin_lock(&cifs_tcp_ses_lock);
138 ses = smb2_find_smb_ses_unlocked(server, ses_id);
130 spin_unlock(&cifs_tcp_ses_lock); 139 spin_unlock(&cifs_tcp_ses_lock);
131 140
141 return ses;
142}
143
144static struct cifs_tcon *
145smb2_find_smb_sess_tcon_unlocked(struct cifs_ses *ses, __u32 tid)
146{
147 struct cifs_tcon *tcon;
148
149 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
150 if (tcon->tid != tid)
151 continue;
152 ++tcon->tc_count;
153 return tcon;
154 }
155
132 return NULL; 156 return NULL;
133} 157}
134 158
159/*
160 * Obtain tcon corresponding to the tid in the given
161 * cifs_ses
162 */
163
164struct cifs_tcon *
165smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid)
166{
167 struct cifs_ses *ses;
168 struct cifs_tcon *tcon;
169
170 spin_lock(&cifs_tcp_ses_lock);
171 ses = smb2_find_smb_ses_unlocked(server, ses_id);
172 if (!ses) {
173 spin_unlock(&cifs_tcp_ses_lock);
174 return NULL;
175 }
176 tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid);
177 spin_unlock(&cifs_tcp_ses_lock);
178
179 return tcon;
180}
181
135int 182int
136smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) 183smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
137{ 184{
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 526f0533cb4e..f6e13a977fc8 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -752,9 +752,11 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
752 752
753 rc = wait_for_response(ses->server, midQ); 753 rc = wait_for_response(ses->server, midQ);
754 if (rc != 0) { 754 if (rc != 0) {
755 cifs_dbg(FYI, "Cancelling wait for mid %llu\n", midQ->mid);
755 send_cancel(ses->server, rqst, midQ); 756 send_cancel(ses->server, rqst, midQ);
756 spin_lock(&GlobalMid_Lock); 757 spin_lock(&GlobalMid_Lock);
757 if (midQ->mid_state == MID_REQUEST_SUBMITTED) { 758 if (midQ->mid_state == MID_REQUEST_SUBMITTED) {
759 midQ->mid_flags |= MID_WAIT_CANCELLED;
758 midQ->callback = DeleteMidQEntry; 760 midQ->callback = DeleteMidQEntry;
759 spin_unlock(&GlobalMid_Lock); 761 spin_unlock(&GlobalMid_Lock);
760 add_credits(ses->server, 1, optype); 762 add_credits(ses->server, 1, optype);