1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2018 Nexenta Systems, Inc. All rights reserved. 14 */ 15 16 /* 17 * Dispatch function for SMB2_CANCEL 18 */ 19 20 #include <smbsrv/smb2_kproto.h> 21 22 static void smb2_cancel_async(smb_request_t *); 23 static void smb2_cancel_sync(smb_request_t *); 24 25 /* 26 * This handles an SMB2_CANCEL request when seen in the reader. 27 * (See smb2sr_newrq) Handle this immediately, rather than 28 * going through the normal taskq dispatch mechanism. 29 * Note that Cancel does NOT get a response. 30 * 31 * Any non-zero return causes disconnect. 32 * SMB2 header is already decoded. 33 */ 34 int 35 smb2_newrq_cancel(smb_request_t *sr) 36 { 37 38 /* 39 * If we get SMB2 cancel as part of a compound, 40 * that's a protocol violation. Drop 'em! 41 */ 42 if (sr->smb2_next_command != 0) 43 return (EINVAL); 44 45 DTRACE_SMB2_START(op__Cancel, smb_request_t *, sr); 46 47 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) 48 smb2_cancel_async(sr); 49 else 50 smb2_cancel_sync(sr); 51 52 DTRACE_SMB2_DONE(op__Cancel, smb_request_t *, sr); 53 54 return (0); 55 } 56 57 /* 58 * Dispatch handler for SMB2_CANCEL. 59 * Note that Cancel does NOT get a response. 60 */ 61 smb_sdrc_t 62 smb2_cancel(smb_request_t *sr) 63 { 64 65 /* 66 * If we get SMB2 cancel as part of a compound, 67 * that's a protocol violation. Drop 'em! 68 */ 69 if (sr->smb2_cmd_hdr != 0 || sr->smb2_next_command != 0) 70 return (SDRC_DROP_VC); 71 72 DTRACE_SMB2_START(op__Cancel, smb_request_t *, sr); 73 74 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { 75 smb2_cancel_async(sr); 76 } else { 77 smb2_cancel_sync(sr); 78 } 79 80 DTRACE_SMB2_DONE(op__Cancel, smb_request_t *, sr); 81 82 return (SDRC_NO_REPLY); 83 } 84 85 /* 86 * SMB2 Cancel (sync) has an inherent race with the request being 87 * cancelled. The request may have been received but not yet 88 * executed by a worker thread, in which case we'll mark the 89 * request state as cancelled, and when a worker thread starts 90 * on this request we'll cancel everything in the compound. 91 */ 92 static void 93 smb2_cancel_sync(smb_request_t *sr) 94 { 95 struct smb_request *req; 96 struct smb_session *session = sr->session; 97 int cnt = 0; 98 99 if (sr->smb2_messageid == 0) 100 goto failure; 101 102 smb_slist_enter(&session->s_req_list); 103 for (req = smb_slist_head(&session->s_req_list); req != NULL; 104 req = smb_slist_next(&session->s_req_list, req)) { 105 106 /* never cancel self */ 107 if (req == sr) 108 continue; 109 110 if (sr->smb2_messageid >= req->smb2_first_msgid && 111 sr->smb2_messageid < (req->smb2_first_msgid + 112 req->smb2_total_credits)) { 113 smb_request_cancel(req); 114 cnt++; 115 } 116 } 117 smb_slist_exit(&session->s_req_list); 118 119 if (cnt != 1) { 120 failure: 121 DTRACE_PROBE2(smb2__cancel__error, 122 uint64_t, sr->smb2_messageid, int, cnt); 123 #ifdef DEBUG 124 /* 125 * It's somewhat common that we may see a cancel for a 126 * request that has already completed, so report that 127 * only in debug builds. 128 */ 129 cmn_err(CE_WARN, "SMB2 cancel failed, " 130 "client=%s, MID=0x%llx", 131 sr->session->ip_addr_str, 132 (u_longlong_t)sr->smb2_messageid); 133 #endif 134 } 135 } 136 137 /* 138 * Note that cancelling an async request doesn't have a race 139 * because the client doesn't learn about the async ID until we 140 * send it to them in an interim reply, and by that point the 141 * request has progressed to the point where smb_cancel can find 142 * the request and cancel it. 143 */ 144 static void 145 smb2_cancel_async(smb_request_t *sr) 146 { 147 struct smb_request *req; 148 struct smb_session *session = sr->session; 149 int cnt = 0; 150 151 smb_slist_enter(&session->s_req_list); 152 req = smb_slist_head(&session->s_req_list); 153 while (req) { 154 ASSERT(req->sr_magic == SMB_REQ_MAGIC); 155 if ((req != sr) && 156 (req->smb2_async_id == sr->smb2_async_id)) { 157 smb_request_cancel(req); 158 cnt++; 159 } 160 req = smb_slist_next(&session->s_req_list, req); 161 } 162 if (cnt != 1) { 163 DTRACE_PROBE2(smb2__cancel__error, 164 uint64_t, sr->smb2_async_id, int, cnt); 165 /* 166 * Not logging here, as this is normal, i.e. 167 * when both a cancel and a handle close 168 * terminates an SMB2_notify request. 169 */ 170 } 171 smb_slist_exit(&session->s_req_list); 172 } 173