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 mutex_enter(&node->n_mutex); 167 168 switch (node->n_state) { 169 case SMB_NODE_STATE_OPLOCK_GRANTED: 170 if (SMB_SESSION_GET_ID(session) == ol->ol_sess_id) { 171 mutex_exit(&node->n_mutex); 172 return; 173 } 174 break; 175 case SMB_NODE_STATE_AVAILABLE: 176 case SMB_NODE_STATE_OPLOCK_BREAKING: 177 break; 178 default: 179 SMB_PANIC(); 180 } 181 182 for (;;) { 183 int rc; 184 185 smb_oplock_wait(node); 186 187 if (node->n_state == SMB_NODE_STATE_AVAILABLE) { 188 if ((op->op_oplock_level == SMB_OPLOCK_LEVEL_II) || 189 (op->op_oplock_level == SMB_OPLOCK_NONE) || 190 (node->n_open_count > 1)) { 191 mutex_exit(&node->n_mutex); 192 op->op_oplock_level = SMB_OPLOCK_NONE; 193 return; 194 } 195 ol->ol_ofile = of; 196 ol->ol_sess_id = SMB_SESSION_GET_ID(session); 197 ol->ol_level = op->op_oplock_level; 198 ol->ol_xthread = curthread; 199 node->n_state = SMB_NODE_STATE_OPLOCK_GRANTED; 200 mutex_exit(&node->n_mutex); 201 if (smb_fsop_oplock_install(node, of->f_mode) == 0) { 202 smb_ofile_set_oplock_granted(of); 203 return; 204 } 205 mutex_enter(&node->n_mutex); 206 ASSERT(node->n_state == SMB_NODE_STATE_OPLOCK_GRANTED); 207 node->n_state = SMB_NODE_STATE_AVAILABLE; 208 ol->ol_xthread = NULL; 209 op->op_oplock_level = SMB_OPLOCK_NONE; 210 cv_broadcast(&ol->ol_cv); 211 break; 212 } 213 214 if (node->n_state == SMB_NODE_STATE_OPLOCK_GRANTED) { 215 if (SMB_SESSION_GET_ID(session) == ol->ol_sess_id) 216 break; 217 node->n_state = SMB_NODE_STATE_OPLOCK_BREAKING; 218 mutex_exit(&node->n_mutex); 219 smb_session_oplock_break( 220 SMB_OFILE_GET_SESSION(ol->ol_ofile), ol->ol_ofile); 221 mutex_enter(&node->n_mutex); 222 continue; 223 } 224 225 ASSERT(node->n_state == SMB_NODE_STATE_OPLOCK_BREAKING); 226 227 rc = cv_timedwait(&ol->ol_cv, &node->n_mutex, time); 228 229 if (rc == -1) { 230 /* 231 * Oplock release timed out. 232 */ 233 if (node->n_state == SMB_NODE_STATE_OPLOCK_BREAKING) { 234 node->n_state = SMB_NODE_STATE_AVAILABLE; 235 ol->ol_xthread = curthread; 236 mutex_exit(&node->n_mutex); 237 smb_fsop_oplock_uninstall(node); 238 mutex_enter(&node->n_mutex); 239 ol->ol_xthread = NULL; 240 cv_broadcast(&ol->ol_cv); 241 } 242 } 243 } 244 mutex_exit(&node->n_mutex); 245 } 246 247 /* 248 * smb_oplock_break 249 * 250 * The oplock break may succeed for multiple reasons: file close, oplock 251 * release, holder connection dropped, requesting client disconnect etc. 252 * 253 * Returns: 254 * 255 * B_TRUE The oplock is broken. 256 * B_FALSE The oplock is being broken. This is returned if nowait is set 257 * to B_TRUE; 258 */ 259 boolean_t 260 smb_oplock_break(smb_node_t *node, smb_session_t *session, boolean_t nowait) 261 { 262 smb_oplock_t *ol; 263 clock_t time; 264 265 SMB_NODE_VALID(node); 266 ol = &node->n_oplock; 267 time = MSEC_TO_TICK(smb_oplock_timeout) + ddi_get_lbolt(); 268 269 if (session != NULL) { 270 mutex_enter(&node->n_mutex); 271 if (SMB_SESSION_GET_ID(session) == ol->ol_sess_id) { 272 mutex_exit(&node->n_mutex); 273 return (B_TRUE); 274 } 275 } else { 276 mutex_enter(&node->n_mutex); 277 } 278 279 for (;;) { 280 int rc; 281 282 smb_oplock_wait(node); 283 284 if (node->n_state == SMB_NODE_STATE_AVAILABLE) { 285 mutex_exit(&node->n_mutex); 286 return (B_TRUE); 287 } 288 289 if (node->n_state == SMB_NODE_STATE_OPLOCK_GRANTED) { 290 node->n_state = SMB_NODE_STATE_OPLOCK_BREAKING; 291 mutex_exit(&node->n_mutex); 292 smb_session_oplock_break( 293 SMB_OFILE_GET_SESSION(ol->ol_ofile), ol->ol_ofile); 294 mutex_enter(&node->n_mutex); 295 continue; 296 } 297 298 ASSERT(node->n_state == SMB_NODE_STATE_OPLOCK_BREAKING); 299 if (nowait) { 300 mutex_exit(&node->n_mutex); 301 return (B_FALSE); 302 } 303 rc = cv_timedwait(&ol->ol_cv, &node->n_mutex, time); 304 if (rc == -1) { 305 /* 306 * Oplock release timed out. 307 */ 308 if (node->n_state == SMB_NODE_STATE_OPLOCK_BREAKING) { 309 node->n_state = SMB_NODE_STATE_AVAILABLE; 310 ol->ol_xthread = curthread; 311 mutex_exit(&node->n_mutex); 312 smb_fsop_oplock_uninstall(node); 313 mutex_enter(&node->n_mutex); 314 ol->ol_xthread = NULL; 315 cv_broadcast(&ol->ol_cv); 316 break; 317 } 318 } 319 } 320 mutex_exit(&node->n_mutex); 321 return (B_TRUE); 322 } 323 324 /* 325 * smb_oplock_release 326 * 327 * This function releases the oplock on the node passed in. If other threads 328 * were waiting for the oplock to be released they are signaled. 329 */ 330 void 331 smb_oplock_release(smb_node_t *node, smb_ofile_t *of) 332 { 333 smb_oplock_t *ol; 334 335 SMB_NODE_VALID(node); 336 ol = &node->n_oplock; 337 338 mutex_enter(&node->n_mutex); 339 smb_oplock_wait(node); 340 switch (node->n_state) { 341 case SMB_NODE_STATE_AVAILABLE: 342 break; 343 344 case SMB_NODE_STATE_OPLOCK_GRANTED: 345 case SMB_NODE_STATE_OPLOCK_BREAKING: 346 if (ol->ol_ofile == of) { 347 node->n_state = SMB_NODE_STATE_AVAILABLE; 348 ol->ol_xthread = curthread; 349 mutex_exit(&node->n_mutex); 350 smb_fsop_oplock_uninstall(node); 351 mutex_enter(&node->n_mutex); 352 ol->ol_xthread = NULL; 353 cv_broadcast(&ol->ol_cv); 354 } 355 break; 356 357 default: 358 SMB_PANIC(); 359 } 360 mutex_exit(&node->n_mutex); 361 } 362 363 /* 364 * smb_oplock_conflict 365 * 366 * The two checks on "session" and "op" are primarily for the open path. 367 * Other SMB functions may call smb_oplock_conflict() with a session 368 * pointer so as to do the session check. 369 */ 370 boolean_t 371 smb_oplock_conflict(smb_node_t *node, smb_session_t *session, open_param_t *op) 372 { 373 boolean_t rb; 374 375 SMB_NODE_VALID(node); 376 SMB_SESSION_VALID(session); 377 378 mutex_enter(&node->n_mutex); 379 smb_oplock_wait(node); 380 switch (node->n_state) { 381 case SMB_NODE_STATE_AVAILABLE: 382 rb = B_FALSE; 383 break; 384 385 case SMB_NODE_STATE_OPLOCK_GRANTED: 386 case SMB_NODE_STATE_OPLOCK_BREAKING: 387 if (SMB_SESSION_GET_ID(session) == node->n_oplock.ol_sess_id) { 388 rb = B_FALSE; 389 break; 390 } 391 392 if (op != NULL) { 393 if (((op->desired_access & ~(FILE_READ_ATTRIBUTES | 394 FILE_WRITE_ATTRIBUTES | SYNCHRONIZE)) == 0) && 395 (op->create_disposition != FILE_SUPERSEDE) && 396 (op->create_disposition != FILE_OVERWRITE)) { 397 /* Attributs only */ 398 rb = B_FALSE; 399 break; 400 } 401 } 402 rb = B_TRUE; 403 break; 404 405 default: 406 SMB_PANIC(); 407 } 408 mutex_exit(&node->n_mutex); 409 return (rb); 410 } 411 412 /* 413 * smb_oplock_broadcast 414 * 415 * The the calling thread has the pointer to its context stored in ol_thread 416 * it resets that field. If any other thread is waiting for that field to 417 * turn to NULL it is signaled. 418 * 419 * Returns: 420 * B_TRUE Oplock unlocked 421 * B_FALSE Oplock still locked 422 */ 423 boolean_t 424 smb_oplock_broadcast(smb_node_t *node) 425 { 426 smb_oplock_t *ol; 427 boolean_t rb; 428 429 SMB_NODE_VALID(node); 430 ol = &node->n_oplock; 431 rb = B_FALSE; 432 433 mutex_enter(&node->n_mutex); 434 if ((ol->ol_xthread != NULL) && (ol->ol_xthread == curthread)) { 435 ol->ol_xthread = NULL; 436 cv_broadcast(&ol->ol_cv); 437 rb = B_TRUE; 438 } 439 mutex_exit(&node->n_mutex); 440 return (rb); 441 } 442 443 /* 444 * smb_oplock_wait 445 * 446 * The mutex of the node must have been entered before calling this function. 447 * If the field ol_xthread is not NULL and doesn't contain the pointer to the 448 * context of the calling thread, the caller will sleep until that field is 449 * reset (set to NULL). 450 */ 451 static void 452 smb_oplock_wait(smb_node_t *node) 453 { 454 smb_oplock_t *ol = &node->n_oplock; 455 456 if ((ol->ol_xthread != NULL) && (ol->ol_xthread != curthread)) { 457 while (ol->ol_xthread != NULL) 458 cv_wait(&ol->ol_cv, &node->n_mutex); 459 } 460 } 461