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 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) 46 smb2_cancel_async(sr); 47 else 48 smb2_cancel_sync(sr); 49 50 return (0); 51 } 52 53 /* 54 * Dispatch handler for SMB2_CANCEL. 55 * Note that Cancel does NOT get a response. 56 */ 57 smb_sdrc_t 58 smb2_cancel(smb_request_t *sr) 59 { 60 61 /* 62 * If we get SMB2 cancel as part of a compound, 63 * that's a protocol violation. Drop 'em! 64 */ 65 if (sr->smb2_cmd_hdr != 0 || sr->smb2_next_command != 0) 66 return (SDRC_DROP_VC); 67 68 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { 69 smb2_cancel_async(sr); 70 } else { 71 smb2_cancel_sync(sr); 72 } 73 74 return (SDRC_NO_REPLY); 75 } 76 77 /* 78 * SMB2 Cancel (sync) has an inherent race with the request being 79 * cancelled. The request may have been received but not yet 80 * executed by a worker thread, in which case we'll mark the 81 * request state as cancelled, and when a worker thread starts 82 * on this request we'll cancel everything in the compound. 83 */ 84 static void 85 smb2_cancel_sync(smb_request_t *sr) 86 { 87 struct smb_request *req; 88 struct smb_session *session = sr->session; 89 int cnt = 0; 90 91 if (sr->smb2_messageid == 0) 92 goto failure; 93 94 smb_slist_enter(&session->s_req_list); 95 for (req = smb_slist_head(&session->s_req_list); req != NULL; 96 req = smb_slist_next(&session->s_req_list, req)) { 97 98 /* never cancel self */ 99 if (req == sr) 100 continue; 101 102 if (sr->smb2_messageid >= req->smb2_first_msgid && 103 sr->smb2_messageid < (req->smb2_first_msgid + 104 req->smb2_total_credits)) { 105 smb_request_cancel(req); 106 cnt++; 107 } 108 } 109 smb_slist_exit(&session->s_req_list); 110 111 if (cnt != 1) { 112 failure: 113 DTRACE_PROBE2(smb2__cancel__error, 114 uint64_t, sr->smb2_messageid, int, cnt); 115 #ifdef DEBUG 116 /* 117 * It's somewhat common that we may see a cancel for a 118 * request that has already completed, so report that 119 * only in debug builds. 120 */ 121 cmn_err(CE_WARN, "SMB2 cancel failed, " 122 "client=%s, MID=0x%llx", 123 sr->session->ip_addr_str, 124 (u_longlong_t)sr->smb2_messageid); 125 #endif 126 } 127 } 128 129 /* 130 * Note that cancelling an async request doesn't have a race 131 * because the client doesn't learn about the async ID until we 132 * send it to them in an interim reply, and by that point the 133 * request has progressed to the point where smb_cancel can find 134 * the request and cancel it. 135 */ 136 static void 137 smb2_cancel_async(smb_request_t *sr) 138 { 139 struct smb_request *req; 140 struct smb_session *session = sr->session; 141 int cnt = 0; 142 143 smb_slist_enter(&session->s_req_list); 144 req = smb_slist_head(&session->s_req_list); 145 while (req) { 146 ASSERT(req->sr_magic == SMB_REQ_MAGIC); 147 if ((req != sr) && 148 (req->smb2_async_id == sr->smb2_async_id)) { 149 smb_request_cancel(req); 150 cnt++; 151 } 152 req = smb_slist_next(&session->s_req_list, req); 153 } 154 if (cnt != 1) { 155 DTRACE_PROBE2(smb2__cancel__error, 156 uint64_t, sr->smb2_async_id, int, cnt); 157 /* 158 * Not logging here, as this is normal, i.e. 159 * when both a cancel and a handle close 160 * terminates an SMB2_notify request. 161 */ 162 } 163 smb_slist_exit(&session->s_req_list); 164 } 165