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 2014 Nexenta Systems, Inc. All rights reserved. 14 */ 15 16 /* 17 * Dispatch function for SMB2_LOCK 18 */ 19 20 #include <smbsrv/smb2_kproto.h> 21 22 struct SMB2_LOCK_ELEMENT { 23 uint64_t Offset; 24 uint64_t Length; 25 uint32_t Flags; 26 uint32_t reserved; 27 }; 28 29 static smb_sdrc_t smb2_lock_async(smb_request_t *); 30 static uint32_t smb2_lock_exec(smb_request_t *, uint16_t); 31 static uint32_t smb2_lock_elem(smb_request_t *, struct SMB2_LOCK_ELEMENT *); 32 33 /* 34 * This is a somewhat arbitrary sanity limit on the length of the 35 * SMB2_LOCK_ELEMENT array. It usually has length one or two. 36 */ 37 int smb2_lock_max_elem = 1024; 38 39 smb_sdrc_t 40 smb2_lock(smb_request_t *sr) 41 { 42 struct SMB2_LOCK_ELEMENT elem; 43 smb2fid_t smb2fid; 44 uint32_t save_offset; 45 uint32_t LockSequence; 46 uint32_t status; 47 uint16_t StructSize; 48 uint16_t LockCount; 49 uint16_t i; 50 boolean_t MayBlock = B_FALSE; 51 int rc = 0; 52 53 /* 54 * SMB2 Lock request 55 */ 56 rc = smb_mbc_decodef( 57 &sr->smb_data, "wwlqq", 58 &StructSize, /* w */ 59 &LockCount, /* w */ 60 &LockSequence, /* l */ 61 &smb2fid.persistent, /* q */ 62 &smb2fid.temporal); /* q */ 63 if (rc || StructSize != 48) 64 return (SDRC_ERROR); 65 66 status = smb2sr_lookup_fid(sr, &smb2fid); 67 if (status) 68 goto errout; 69 if (sr->fid_ofile->f_node == NULL || LockCount == 0) { 70 status = NT_STATUS_INVALID_PARAMETER; 71 goto errout; 72 } 73 if (LockCount > smb2_lock_max_elem) { 74 status = NT_STATUS_INSUFFICIENT_RESOURCES; 75 goto errout; 76 } 77 78 /* 79 * Process the array of SMB2_LOCK_ELEMENT structs 80 * We do this twice. (it's always a short list) 81 * The first time, just validate the flags, and check 82 * if any of the locking request might need to block. 83 * The second time (either here, or in the async 84 * handler function) process the locks for real. 85 */ 86 save_offset = sr->smb_data.chain_offset; 87 for (i = 0; i < LockCount; i++) { 88 rc = smb_mbc_decodef( 89 &sr->smb_data, "qqll", 90 &elem.Offset, /* q */ 91 &elem.Length, /* q */ 92 &elem.Flags, /* l */ 93 &elem.reserved); /* l */ 94 if (rc) { 95 status = NT_STATUS_INVALID_PARAMETER; 96 goto errout; 97 } 98 99 /* 100 * Make sure the flags are valid; 101 * Find out if we might block. 102 */ 103 switch (elem.Flags) { 104 case SMB2_LOCKFLAG_SHARED_LOCK: 105 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: 106 MayBlock = B_TRUE; 107 break; 108 109 /* BEGIN CSTYLED */ 110 case SMB2_LOCKFLAG_SHARED_LOCK | 111 SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 112 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK | 113 SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 114 case SMB2_LOCKFLAG_UNLOCK: 115 /* END CSTYLED */ 116 break; 117 118 default: 119 status = NT_STATUS_INVALID_PARAMETER; 120 goto errout; 121 } 122 } 123 124 if (MayBlock) { 125 /* 126 * May need to block. "Go async". 127 */ 128 status = smb2sr_go_async(sr, smb2_lock_async); 129 goto errout; 130 } 131 132 sr->smb_data.chain_offset = save_offset; 133 status = smb2_lock_exec(sr, LockCount); 134 if (status) 135 goto errout; 136 137 /* 138 * SMB2 Lock reply (sync) 139 */ 140 StructSize = 4; 141 (void) smb_mbc_encodef( 142 &sr->reply, "w..", 143 StructSize); /* w */ 144 /* reserved .. */ 145 return (SDRC_SUCCESS); 146 147 errout: 148 smb2sr_put_error(sr, status); 149 return (SDRC_SUCCESS); 150 } 151 152 static smb_sdrc_t 153 smb2_lock_async(smb_request_t *sr) 154 { 155 smb2fid_t smb2fid; 156 uint32_t LockSequence; 157 uint32_t status; 158 uint16_t StructSize; 159 uint16_t LockCount; 160 int rc = 0; 161 162 /* 163 * Decode the lock request again. It should all decode 164 * exactly the same as the first time we saw it. If not, 165 * report an "internal error". 166 */ 167 rc = smb_mbc_decodef( 168 &sr->smb_data, "wwlqq", 169 &StructSize, /* w */ 170 &LockCount, /* w */ 171 &LockSequence, /* l */ 172 &smb2fid.persistent, /* q */ 173 &smb2fid.temporal); /* q */ 174 if (rc || StructSize != 48) 175 return (SDRC_ERROR); 176 177 status = smb2sr_lookup_fid(sr, &smb2fid); 178 if (status) 179 goto errout; 180 if (sr->fid_ofile->f_node == NULL || LockCount == 0) { 181 status = NT_STATUS_INTERNAL_ERROR; 182 goto errout; 183 } 184 185 status = smb2_lock_exec(sr, LockCount); 186 if (status) 187 goto errout; 188 189 /* 190 * SMB2 Lock reply (async) 191 */ 192 StructSize = 4; 193 (void) smb_mbc_encodef( 194 &sr->reply, "w..", 195 StructSize); /* w */ 196 /* reserved .. */ 197 return (SDRC_SUCCESS); 198 199 errout: 200 smb2sr_put_error(sr, status); 201 return (SDRC_SUCCESS); 202 } 203 204 /* 205 * Execute the vector of locks. This is the common function called by 206 * either the sync or async code paths. We've already decoded this 207 * request once when we get here, so if there are any decode errors 208 * then it's some kind of internal error. 209 */ 210 static uint32_t 211 smb2_lock_exec(smb_request_t *sr, uint16_t LockCount) 212 { 213 struct SMB2_LOCK_ELEMENT elem; 214 uint32_t status = 0; 215 uint16_t i; 216 int rc; 217 218 /* 219 * On entry, out position in the input data should be 220 * after both the SMB2 header and the fixed part of 221 * the SMB Lock request header (24). 222 */ 223 ASSERT(sr->smb_data.chain_offset == 224 (sr->smb2_cmd_hdr + SMB2_HDR_SIZE + 24)); 225 226 /* 227 * This is checked by our callers, but let's make sure. 228 */ 229 ASSERT(sr->fid_ofile->f_node != NULL); 230 231 for (i = 0; i < LockCount; i++) { 232 rc = smb_mbc_decodef( 233 &sr->smb_data, "qqll", 234 &elem.Offset, /* q */ 235 &elem.Length, /* q */ 236 &elem.Flags, /* l */ 237 &elem.reserved); /* l */ 238 if (rc) { 239 status = NT_STATUS_INTERNAL_ERROR; 240 break; 241 } 242 status = smb2_lock_elem(sr, &elem); 243 if (status) 244 break; 245 } 246 return (status); 247 } 248 249 static uint32_t 250 smb2_lock_elem(smb_request_t *sr, struct SMB2_LOCK_ELEMENT *elem) 251 { 252 smb_node_t *node = sr->fid_ofile->f_node; 253 uint32_t status; 254 uint32_t ltype; 255 uint32_t timeout = 0; 256 257 switch (elem->Flags) { 258 case SMB2_LOCKFLAG_SHARED_LOCK: 259 timeout = UINT_MAX; 260 /* FALLTHROUGH */ 261 case SMB2_LOCKFLAG_SHARED_LOCK | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 262 ltype = SMB_LOCK_TYPE_READONLY; 263 status = smb_lock_range(sr, 264 elem->Offset, elem->Length, 265 timeout, ltype); 266 break; 267 268 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK: 269 timeout = UINT_MAX; 270 /* FALLTHROUGH */ 271 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK | SMB2_LOCKFLAG_FAIL_IMMEDIATELY: 272 ltype = SMB_LOCK_TYPE_READWRITE; 273 status = smb_lock_range(sr, 274 elem->Offset, elem->Length, 275 timeout, ltype); 276 break; 277 278 case SMB2_LOCKFLAG_UNLOCK: 279 status = smb_unlock_range(sr, node, 280 elem->Offset, elem->Length); 281 break; 282 283 /* 284 * We've already checked the flags previously, so any 285 * surprises here are some kind of internal error. 286 */ 287 default: 288 status = NT_STATUS_INTERNAL_ERROR; 289 break; 290 } 291 292 return (status); 293 } 294