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