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 * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 /*
27 * smb1 oplock support
28 */
29
30 #include <smbsrv/smb_kproto.h>
31
32 #define BATCH_OR_EXCL (OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE)
33
34 /*
35 * Client has an open handle and requests an oplock.
36 * Convert SMB1 oplock request info in to internal form,
37 * call common oplock code, convert result to SMB1.
38 */
39 void
40 smb1_oplock_acquire(smb_request_t *sr, boolean_t level2ok)
41 {
42 smb_arg_open_t *op = &sr->arg.open;
43 smb_ofile_t *ofile = sr->fid_ofile;
44 uint32_t status;
45
46 /* Only disk trees get oplocks. */
47 if ((sr->tid_tree->t_res_type & STYPE_MASK) != STYPE_DISKTREE) {
48 op->op_oplock_level = SMB_OPLOCK_NONE;
49 return;
50 }
51
52 if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
53 op->op_oplock_level = SMB_OPLOCK_NONE;
54 return;
55 }
56
57 if (!smb_session_levelII_oplocks(sr->session))
58 level2ok = B_FALSE;
59
60 /* Common code checks file type. */
61
62 /*
63 * SMB1: Convert to internal form.
64 */
65 switch (op->op_oplock_level) {
66 case SMB_OPLOCK_BATCH:
67 op->op_oplock_state = OPLOCK_LEVEL_BATCH;
68 break;
69 case SMB_OPLOCK_EXCLUSIVE:
70 op->op_oplock_state = OPLOCK_LEVEL_ONE;
71 break;
72 case SMB_OPLOCK_LEVEL_II:
73 op->op_oplock_state = OPLOCK_LEVEL_TWO;
74 break;
75 case SMB_OPLOCK_NONE:
76 default:
77 op->op_oplock_level = SMB_OPLOCK_NONE;
78 return;
79 }
80
81 /*
82 * Tree options may force shared oplocks
83 */
84 if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
85 op->op_oplock_state = OPLOCK_LEVEL_TWO;
86 }
87
88 /*
89 * Try exclusive first, if requested
90 */
91 if ((op->op_oplock_state & BATCH_OR_EXCL) != 0) {
92 status = smb_oplock_request(sr, ofile,
93 &op->op_oplock_state);
94 } else {
95 status = NT_STATUS_OPLOCK_NOT_GRANTED;
96 }
97
98 /*
99 * If exclusive failed (or tree forced shared oplocks)
100 * and if the caller supports Level II, try shared.
101 */
102 if (status == NT_STATUS_OPLOCK_NOT_GRANTED && level2ok) {
103 op->op_oplock_state = OPLOCK_LEVEL_TWO;
104 status = smb_oplock_request(sr, ofile,
105 &op->op_oplock_state);
106 }
107
108 /*
109 * Either of the above may have returned the
110 * status code that says we should wait.
111 */
112 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
113 (void) smb_oplock_wait_break(ofile->f_node, 0);
114 status = 0;
115 }
116
117 /*
118 * Keep track of what we got (in ofile->f_oplock.og_state)
119 * so we'll know what we had when sending a break later.
120 * The og_dialect here is the oplock dialect, which may be
121 * different than SMB dialect. Pre-NT clients did not
122 * support "Level II" oplocks. If we're talking to a
123 * client that didn't set the CAP_LEVEL_II_OPLOCKS in
124 * its capabilities, let og_dialect = LANMAN2_1.
125 */
126 ofile->f_oplock.og_dialect = (level2ok) ?
127 NT_LM_0_12 : LANMAN2_1;
128 switch (status) {
129 case NT_STATUS_SUCCESS:
130 ofile->f_oplock.og_state = op->op_oplock_state;
131 break;
132 case NT_STATUS_OPLOCK_NOT_GRANTED:
133 ofile->f_oplock.og_state = 0;
134 op->op_oplock_level = SMB_OPLOCK_NONE;
135 return;
136 default:
137 /* Caller did not check args sufficiently? */
138 cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x",
139 sr->session->ip_addr_str, status);
140 ofile->f_oplock.og_state = 0;
141 op->op_oplock_level = SMB_OPLOCK_NONE;
142 return;
143 }
144
145 /*
146 * Have STATUS_SUCCESS
147 * Convert internal oplock state to SMB1
148 */
149 if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
150 op->op_oplock_level = SMB_OPLOCK_BATCH;
151 } else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
152 op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE;
153 } else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
154 op->op_oplock_level = SMB_OPLOCK_LEVEL_II;
155 } else {
156 op->op_oplock_level = SMB_OPLOCK_NONE;
157 }
158 }