1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2017 Nexenta Systems, Inc. All rights reserved.
*/
/*
* smb1 oplock support
*/
#include <smbsrv/smb_kproto.h>
#define BATCH_OR_EXCL (OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE)
/*
* Client has an open handle and requests an oplock.
* Convert SMB1 oplock request info in to internal form,
* call common oplock code, convert result to SMB1.
*/
void
smb1_oplock_acquire(smb_request_t *sr, boolean_t level2ok)
{
smb_arg_open_t *op = &sr->arg.open;
smb_ofile_t *ofile = sr->fid_ofile;
uint32_t status;
/* Only disk trees get oplocks. */
if ((sr->tid_tree->t_res_type & STYPE_MASK) != STYPE_DISKTREE) {
op->op_oplock_level = SMB_OPLOCK_NONE;
return;
}
if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
op->op_oplock_level = SMB_OPLOCK_NONE;
return;
}
if (!smb_session_levelII_oplocks(sr->session))
level2ok = B_FALSE;
/* Common code checks file type. */
/*
* SMB1: Convert to internal form.
*/
switch (op->op_oplock_level) {
case SMB_OPLOCK_BATCH:
op->op_oplock_state = OPLOCK_LEVEL_BATCH;
break;
case SMB_OPLOCK_EXCLUSIVE:
op->op_oplock_state = OPLOCK_LEVEL_ONE;
break;
case SMB_OPLOCK_LEVEL_II:
op->op_oplock_state = OPLOCK_LEVEL_TWO;
break;
case SMB_OPLOCK_NONE:
default:
op->op_oplock_level = SMB_OPLOCK_NONE;
return;
}
/*
* Tree options may force shared oplocks
*/
if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
op->op_oplock_state = OPLOCK_LEVEL_TWO;
}
/*
* Try exclusive first, if requested
*/
if ((op->op_oplock_state & BATCH_OR_EXCL) != 0) {
status = smb_oplock_request(sr, ofile,
&op->op_oplock_state);
} else {
status = NT_STATUS_OPLOCK_NOT_GRANTED;
}
/*
* If exclusive failed (or tree forced shared oplocks)
* and if the caller supports Level II, try shared.
*/
if (status == NT_STATUS_OPLOCK_NOT_GRANTED && level2ok) {
op->op_oplock_state = OPLOCK_LEVEL_TWO;
status = smb_oplock_request(sr, ofile,
&op->op_oplock_state);
}
/*
* Either of the above may have returned the
* status code that says we should wait.
*/
if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
(void) smb_oplock_wait_break(ofile->f_node, 0);
status = 0;
}
/*
* Keep track of what we got (in ofile->f_oplock.og_state)
* so we'll know what we had when sending a break later.
* The og_dialect here is the oplock dialect, which may be
* different than SMB dialect. Pre-NT clients did not
* support "Level II" oplocks. If we're talking to a
* client that didn't set the CAP_LEVEL_II_OPLOCKS in
* its capabilities, let og_dialect = LANMAN2_1.
*/
ofile->f_oplock.og_dialect = (level2ok) ?
NT_LM_0_12 : LANMAN2_1;
switch (status) {
case NT_STATUS_SUCCESS:
ofile->f_oplock.og_state = op->op_oplock_state;
break;
case NT_STATUS_OPLOCK_NOT_GRANTED:
ofile->f_oplock.og_state = 0;
op->op_oplock_level = SMB_OPLOCK_NONE;
return;
default:
/* Caller did not check args sufficiently? */
cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x",
sr->session->ip_addr_str, status);
ofile->f_oplock.og_state = 0;
op->op_oplock_level = SMB_OPLOCK_NONE;
return;
}
/*
* Have STATUS_SUCCESS
* Convert internal oplock state to SMB1
*/
if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
op->op_oplock_level = SMB_OPLOCK_BATCH;
} else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE;
} else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
op->op_oplock_level = SMB_OPLOCK_LEVEL_II;
} else {
op->op_oplock_level = SMB_OPLOCK_NONE;
}
}
|