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