Home | History | Annotate | Download | only in smbsrv
      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_kproto.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 		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