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