1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * SMB Locking library functions. 27 */ 28 29 #include <smbsrv/smb_incl.h> 30 #include <smbsrv/smb_fsops.h> 31 #include <inet/tcp.h> 32 33 static void smb_oplock_enter(smb_node_t *); 34 35 /* 36 * Magic 0xFF 'S' 'M' 'B' 37 * smb_com a byte, the "first" command 38 * Error a 4-byte union, ignored in a request 39 * smb_flg a one byte set of eight flags 40 * smb_flg2 a two byte set of 16 flags 41 * . twelve reserved bytes, have a role 42 * in connectionless transports (IPX, UDP?) 43 * smb_tid a 16-bit tree ID, a mount point sorta, 44 * 0xFFFF is this command does not have 45 * or require a tree context 46 * smb_pid a 16-bit process ID 47 * smb_uid a 16-bit user ID, specific to this "session" 48 * and mapped to a system (bona-fide) UID 49 * smb_mid a 16-bit multiplex ID, used to differentiate 50 * multiple simultaneous requests from the same 51 * process (pid) (ref RPC "xid") 52 * 53 * SMB_COM_LOCKING_ANDX allows both locking and/or unlocking of file range(s). 54 * 55 * Client Request Description 56 * ================================== ================================= 57 * 58 * UCHAR WordCount; Count of parameter words = 8 59 * UCHAR AndXCommand; Secondary (X) command; 0xFF = none 60 * UCHAR AndXReserved; Reserved (must be 0) 61 * USHORT AndXOffset; Offset to next command WordCount 62 * USHORT Fid; File handle 63 * UCHAR LockType; See LockType table below 64 * UCHAR OplockLevel; The new oplock level 65 * ULONG Timeout; Milliseconds to wait for unlock 66 * USHORT NumberOfUnlocks; Num. unlock range structs following 67 * USHORT NumberOfLocks; Num. lock range structs following 68 * USHORT ByteCount; Count of data bytes 69 * LOCKING_ANDX_RANGE Unlocks[]; Unlock ranges 70 * LOCKING_ANDX_RANGE Locks[]; Lock ranges 71 * 72 * LockType Flag Name Value Description 73 * ============================ ===== ================================ 74 * 75 * LOCKING_ANDX_SHARED_LOCK 0x01 Read-only lock 76 * LOCKING_ANDX_OPLOCK_RELEASE 0x02 Oplock break notification 77 * LOCKING_ANDX_CHANGE_LOCKTYPE 0x04 Change lock type 78 * LOCKING_ANDX_CANCEL_LOCK 0x08 Cancel outstanding request 79 * LOCKING_ANDX_LARGE_FILES 0x10 Large file locking format 80 * 81 * LOCKING_ANDX_RANGE Format 82 * ===================================================================== 83 * 84 * USHORT Pid; PID of process "owning" lock 85 * ULONG Offset; Offset to bytes to [un]lock 86 * ULONG Length; Number of bytes to [un]lock 87 * 88 * Large File LOCKING_ANDX_RANGE Format 89 * ===================================================================== 90 * 91 * USHORT Pid; PID of process "owning" lock 92 * USHORT Pad; Pad to DWORD align (mbz) 93 * ULONG OffsetHigh; Offset to bytes to [un]lock 94 * (high) 95 * ULONG OffsetLow; Offset to bytes to [un]lock (low) 96 * ULONG LengthHigh; Number of bytes to [un]lock 97 * (high) 98 * ULONG LengthLow; Number of bytes to [un]lock (low) 99 * 100 * Server Response Description 101 * ================================== ================================= 102 * 103 * UCHAR WordCount; Count of parameter words = 2 104 * UCHAR AndXCommand; Secondary (X) command; 0xFF = 105 * none 106 * UCHAR AndXReserved; Reserved (must be 0) 107 * USHORT AndXOffset; Offset to next command WordCount 108 * USHORT ByteCount; Count of data bytes = 0 109 * 110 */ 111 112 /* 113 * smb_oplock_acquire 114 * 115 * Attempt to acquire an oplock. Note that the oplock granted may be 116 * none, i.e. the oplock was not granted. The result of the acquisition is 117 * provided in ol->ol_level. 118 * 119 * Grant an oplock to the requestor if this session is the only one 120 * that has the file open, regardless of the number of instances of 121 * the file opened by this session. 122 * 123 * However, if there is no oplock on this file and there is already 124 * at least one open, we will not grant an oplock, even if the only 125 * existing opens are from the same client. This is "server discretion." 126 * 127 * An oplock may need to be broken in order for one to be granted, and 128 * depending on what action is taken by the other client (unlock or close), 129 * an oplock may or may not be granted. (The breaking of an oplock is 130 * done earlier in the calling path.) 131 */ 132 void 133 smb_oplock_acquire(smb_node_t *node, smb_ofile_t *of, open_param_t *op) 134 { 135 smb_session_t *session; 136 smb_oplock_t *ol; 137 clock_t time; 138 139 SMB_NODE_VALID(node); 140 SMB_OFILE_VALID(of); 141 142 ASSERT(node == SMB_OFILE_GET_NODE(of)); 143 144 session = SMB_OFILE_GET_SESSION(of); 145 146 if (!smb_session_oplocks_enable(session) || 147 smb_tree_has_feature(SMB_OFILE_GET_TREE(of), SMB_TREE_NO_OPLOCKS)) { 148 /* This implies that trees cannot overlap. */ 149 op->op_oplock_level = SMB_OPLOCK_NONE; 150 return; 151 } 152 153 ol = &node->n_oplock; 154 time = MSEC_TO_TICK(smb_oplock_timeout) + ddi_get_lbolt(); 155 156 mutex_enter(&node->n_mutex); 157 158 for (;;) { 159 int rc; 160 161 smb_oplock_enter(node); 162 163 if (node->n_state == SMB_NODE_STATE_AVAILABLE) { 164 if ((op->op_oplock_level == SMB_OPLOCK_LEVEL_II) || 165 (op->op_oplock_level == SMB_OPLOCK_NONE) || 166 (node->n_open_count > 1)) { 167 mutex_exit(&node->n_mutex); 168 op->op_oplock_level = SMB_OPLOCK_NONE; 169 return; 170 } 171 ol->ol_ofile = of; 172 ol->ol_sess_id = SMB_SESSION_GET_ID(session); 173 ol->ol_level = op->op_oplock_level; 174 ol->ol_xthread = curthread; 175 node->n_state = SMB_NODE_STATE_OPLOCK_GRANTED; 176 mutex_exit(&node->n_mutex); 177 if (smb_fsop_oplock_install(node, of->f_mode) == 0) { 178 smb_ofile_set_oplock_granted(of); 179 return; 180 } 181 mutex_enter(&node->n_mutex); 182 ASSERT(node->n_state == SMB_NODE_STATE_OPLOCK_GRANTED); 183 node->n_state = SMB_NODE_STATE_AVAILABLE; 184 ol->ol_xthread = NULL; 185 op->op_oplock_level = SMB_OPLOCK_NONE; 186 if (ol->ol_waiters_count != 0) 187 cv_broadcast(&ol->ol_cv); 188 break; 189 } 190 191 if (node->n_state == SMB_NODE_STATE_OPLOCK_GRANTED) { 192 if (SMB_SESSION_GET_ID(session) == ol->ol_sess_id) 193 break; 194 node->n_state = SMB_NODE_STATE_OPLOCK_BREAKING; 195 smb_session_oplock_break( 196 SMB_OFILE_GET_SESSION(ol->ol_ofile), ol->ol_ofile); 197 } 198 199 ASSERT(node->n_state == SMB_NODE_STATE_OPLOCK_BREAKING); 200 201 ol->ol_waiters_count++; 202 rc = cv_timedwait(&ol->ol_cv, &node->n_mutex, time); 203 ol->ol_waiters_count--; 204 205 if (rc == -1) { 206 /* 207 * Oplock release timed out. 208 */ 209 if (node->n_state == SMB_NODE_STATE_OPLOCK_BREAKING) { 210 node->n_state = SMB_NODE_STATE_AVAILABLE; 211 ol->ol_xthread = curthread; 212 mutex_exit(&node->n_mutex); 213 smb_fsop_oplock_uninstall(node); 214 smb_session_oplock_break_timedout( 215 SMB_OFILE_GET_SESSION(ol->ol_ofile)); 216 mutex_enter(&node->n_mutex); 217 ol->ol_xthread = NULL; 218 if (ol->ol_waiters_count != 0) 219 cv_broadcast(&ol->ol_cv); 220 } 221 } 222 } 223 mutex_exit(&node->n_mutex); 224 } 225 226 /* 227 * smb_oplock_break 228 * 229 * The oplock break may succeed for multiple reasons: file close, oplock 230 * release, holder connection dropped, requesting client disconnect etc. 231 * 232 * Returns: 233 * 234 * B_TRUE The oplock is broken. 235 * B_FALSE The oplock is being broken. This is returned if nowait is set 236 * to B_TRUE; 237 */ 238 boolean_t 239 smb_oplock_break(smb_node_t *node, uint64_t sess_id, boolean_t nowait) 240 { 241 smb_oplock_t *ol; 242 clock_t time; 243 244 SMB_NODE_VALID(node); 245 ol = &node->n_oplock; 246 time = MSEC_TO_TICK(smb_oplock_timeout) + ddi_get_lbolt(); 247 248 mutex_enter(&node->n_mutex); 249 if (ol->ol_sess_id == sess_id) { 250 mutex_exit(&node->n_mutex); 251 return (B_TRUE); 252 } 253 254 for (;;) { 255 int rc; 256 257 smb_oplock_enter(node); 258 259 if (node->n_state == SMB_NODE_STATE_AVAILABLE) { 260 mutex_exit(&node->n_mutex); 261 return (B_TRUE); 262 } 263 264 if (node->n_state == SMB_NODE_STATE_OPLOCK_GRANTED) { 265 node->n_state = SMB_NODE_STATE_OPLOCK_BREAKING; 266 smb_session_oplock_break( 267 SMB_OFILE_GET_SESSION(ol->ol_ofile), ol->ol_ofile); 268 } 269 270 ASSERT(node->n_state == SMB_NODE_STATE_OPLOCK_BREAKING); 271 if (nowait) { 272 mutex_exit(&node->n_mutex); 273 return (B_FALSE); 274 } 275 ol->ol_waiters_count++; 276 rc = cv_timedwait(&ol->ol_cv, &node->n_mutex, time); 277 ol->ol_waiters_count--; 278 if (rc == -1) { 279 /* 280 * Oplock release timed out. 281 */ 282 if (node->n_state == SMB_NODE_STATE_OPLOCK_BREAKING) { 283 node->n_state = SMB_NODE_STATE_AVAILABLE; 284 ol->ol_xthread = curthread; 285 mutex_exit(&node->n_mutex); 286 smb_fsop_oplock_uninstall(node); 287 smb_session_oplock_break_timedout( 288 SMB_OFILE_GET_SESSION(ol->ol_ofile)); 289 mutex_enter(&node->n_mutex); 290 ol->ol_xthread = NULL; 291 if (ol->ol_waiters_count != 0) 292 cv_broadcast(&ol->ol_cv); 293 break; 294 } 295 } 296 } 297 mutex_exit(&node->n_mutex); 298 return (B_TRUE); 299 } 300 301 /* 302 * smb_oplock_release 303 * 304 * This function releases the oplock on the node passed in. If other threads 305 * were waiting for the oplock to be released they are signaled. 306 */ 307 void 308 smb_oplock_release(smb_node_t *node, smb_ofile_t *of) 309 { 310 smb_oplock_t *ol; 311 312 SMB_NODE_VALID(node); 313 ol = &node->n_oplock; 314 315 mutex_enter(&node->n_mutex); 316 smb_oplock_enter(node); 317 switch (node->n_state) { 318 case SMB_NODE_STATE_AVAILABLE: 319 break; 320 321 case SMB_NODE_STATE_OPLOCK_GRANTED: 322 case SMB_NODE_STATE_OPLOCK_BREAKING: 323 if (ol->ol_ofile == of) { 324 node->n_state = SMB_NODE_STATE_AVAILABLE; 325 ol->ol_xthread = curthread; 326 mutex_exit(&node->n_mutex); 327 smb_fsop_oplock_uninstall(node); 328 smb_session_oplock_released( 329 SMB_OFILE_GET_SESSION(ol->ol_ofile)); 330 mutex_enter(&node->n_mutex); 331 ol->ol_xthread = NULL; 332 if (ol->ol_waiters_count != 0) 333 cv_broadcast(&ol->ol_cv); 334 } 335 break; 336 337 default: 338 SMB_PANIC(); 339 } 340 mutex_exit(&node->n_mutex); 341 } 342 343 /* 344 * smb_oplock_conflict 345 * 346 * The two checks on "session" and "op" are primarily for the open path. 347 * Other CIFS functions may call smb_oplock_conflict() with a session 348 * pointer so as to do the session check. 349 */ 350 boolean_t 351 smb_oplock_conflict(smb_node_t *node, smb_session_t *session, open_param_t *op) 352 { 353 boolean_t rb; 354 355 SMB_NODE_VALID(node); 356 SMB_SESSION_VALID(session); 357 358 mutex_enter(&node->n_mutex); 359 smb_oplock_enter(node); 360 switch (node->n_state) { 361 case SMB_NODE_STATE_AVAILABLE: 362 rb = B_FALSE; 363 break; 364 365 case SMB_NODE_STATE_OPLOCK_GRANTED: 366 case SMB_NODE_STATE_OPLOCK_BREAKING: 367 if (SMB_SESSION_GET_ID(session) == node->n_oplock.ol_sess_id) { 368 rb = B_FALSE; 369 break; 370 } 371 372 if (op != NULL) { 373 if (((op->desired_access & ~(FILE_READ_ATTRIBUTES | 374 FILE_WRITE_ATTRIBUTES | SYNCHRONIZE)) == 0) && 375 (op->create_disposition != FILE_SUPERSEDE) && 376 (op->create_disposition != FILE_OVERWRITE)) { 377 /* Attributs only */ 378 rb = B_FALSE; 379 break; 380 } 381 } 382 rb = B_TRUE; 383 break; 384 385 default: 386 SMB_PANIC(); 387 } 388 mutex_exit(&node->n_mutex); 389 return (rb); 390 } 391 392 /* 393 * smb_oplock_exit 394 * 395 * The the calling thread has the pointer to its context stored in ol_thread 396 * it resets that field. If any other thread is waiting for that field to 397 * turn to NULL it is signaled. 398 * 399 * Returns: 400 * B_TRUE Oplock unlocked 401 * B_FALSE Oplock still locked 402 */ 403 boolean_t 404 smb_oplock_exit(smb_node_t *node) 405 { 406 smb_oplock_t *ol; 407 boolean_t rb; 408 409 SMB_NODE_VALID(node); 410 ol = &node->n_oplock; 411 rb = B_FALSE; 412 413 mutex_enter(&node->n_mutex); 414 if ((ol->ol_xthread != NULL) && (ol->ol_xthread == curthread)) { 415 ol->ol_xthread = NULL; 416 if (ol->ol_waiters_count != 0) 417 cv_broadcast(&ol->ol_cv); 418 rb = B_TRUE; 419 } 420 mutex_exit(&node->n_mutex); 421 return (rb); 422 } 423 424 /* 425 * smb_oplock_wait 426 * 427 * The mutex of the node must have benn entered before calling this function. 428 * If the field ol_xthread is not NULL and doesn't contain the pointer to the 429 * context of the calling thread, the caller will sleep until that field is 430 * reset (set to NULL). 431 */ 432 static void 433 smb_oplock_enter(smb_node_t *node) 434 { 435 smb_oplock_t *ol = &node->n_oplock; 436 437 if ((ol->ol_xthread != NULL) && (ol->ol_xthread != curthread)) { 438 ol->ol_waiters_count++; 439 while (ol->ol_xthread != NULL) 440 cv_wait(&ol->ol_cv, &node->n_mutex); 441 ol->ol_waiters_count--; 442 } 443 } 444