xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_oplock.c (revision 6e6545bfaed3bab9ce836ee82d1abd8f2edba89a)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
14  */
15 
16 /*
17  * Dispatch function for SMB2_OPLOCK_BREAK
18  */
19 
20 #include <smbsrv/smb2_kproto.h>
21 
22 #define	BATCH_OR_EXCL	(OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE)
23 
24 /* StructSize for the two "break" message formats. */
25 #define	SSZ_OPLOCK	24
26 #define	SSZ_LEASE	36
27 
28 /*
29  * SMB2 Oplock Break Acknowledgement
30  * [MS-SMB2] 3.3.5.22.1 Processing an Oplock Acknowledgment
31  * Called via smb2_disp_table[]
32  */
33 smb_sdrc_t
34 smb2_oplock_break_ack(smb_request_t *sr)
35 {
36 	smb_ofile_t *ofile;
37 	smb2fid_t smb2fid;
38 	uint32_t status;
39 	uint32_t NewLevel;
40 	uint8_t smbOplockLevel;
41 	int rc = 0;
42 	uint16_t StructSize;
43 
44 	/*
45 	 * Decode the SMB2 Oplock Break Ack (24 bytes) or
46 	 * Lease Break Ack (36 bytes), starting with just
47 	 * the StructSize, which tells us what this is.
48 	 */
49 	rc = smb_mbc_decodef(&sr->smb_data, "w", &StructSize);
50 	if (rc != 0)
51 		return (SDRC_ERROR);
52 
53 	if (StructSize == SSZ_LEASE) {
54 		/* See smb2_lease.c */
55 		return (smb2_lease_break_ack(sr));
56 	}
57 	if (StructSize != SSZ_OPLOCK)
58 		return (SDRC_ERROR);
59 
60 	/*
61 	 * Decode an SMB2 Oplock Break Ack.
62 	 * [MS-SMB2] 2.2.24.1
63 	 * Note: Struct size decoded above.
64 	 */
65 	rc = smb_mbc_decodef(
66 	    &sr->smb_data, "b5.qq",
67 	    &smbOplockLevel,		/* b */
68 	    /* reserved			  5. */
69 	    &smb2fid.persistent,	/* q */
70 	    &smb2fid.temporal);		/* q */
71 	if (rc != 0)
72 		return (SDRC_ERROR);
73 
74 	/* Find the ofile */
75 	status = smb2sr_lookup_fid(sr, &smb2fid);
76 	/* Success or NT_STATUS_FILE_CLOSED */
77 
78 	DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr);
79 	if (status != 0)
80 		goto errout;
81 
82 	/*
83 	 * Process an (old-style) oplock break ack.
84 	 */
85 	switch (smbOplockLevel) {
86 	case SMB2_OPLOCK_LEVEL_NONE:	/* 0x00 */
87 		NewLevel = OPLOCK_LEVEL_NONE;
88 		break;
89 	case SMB2_OPLOCK_LEVEL_II:	/* 0x01 */
90 		NewLevel = OPLOCK_LEVEL_TWO;
91 		break;
92 	case SMB2_OPLOCK_LEVEL_EXCLUSIVE: /* 0x08 */
93 		NewLevel = OPLOCK_LEVEL_ONE;
94 		break;
95 	case SMB2_OPLOCK_LEVEL_BATCH:	/* 0x09 */
96 		NewLevel = OPLOCK_LEVEL_BATCH;
97 		break;
98 	case SMB2_OPLOCK_LEVEL_LEASE:	/* 0xFF */
99 	default:
100 		NewLevel = OPLOCK_LEVEL_NONE;
101 		break;
102 	}
103 
104 	ofile = sr->fid_ofile;
105 	ofile->f_oplock.og_breaking = 0;
106 	status = smb_oplock_ack_break(sr, ofile, &NewLevel);
107 	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
108 		status = smb2sr_go_async(sr);
109 		if (status != 0)
110 			goto errout;
111 		(void) smb_oplock_wait_break(ofile->f_node, 0);
112 		status = 0;
113 	}
114 	if (status != 0) {
115 		NewLevel = OPLOCK_LEVEL_NONE;
116 		goto errout;
117 	}
118 
119 	ofile->f_oplock.og_state = NewLevel;
120 	switch (NewLevel & OPLOCK_LEVEL_TYPE_MASK) {
121 	case OPLOCK_LEVEL_NONE:
122 		smbOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
123 		break;
124 	case OPLOCK_LEVEL_TWO:
125 		smbOplockLevel = SMB2_OPLOCK_LEVEL_II;
126 		break;
127 	case OPLOCK_LEVEL_ONE:
128 		smbOplockLevel = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
129 		break;
130 	case OPLOCK_LEVEL_BATCH:
131 		smbOplockLevel = SMB2_OPLOCK_LEVEL_BATCH;
132 		break;
133 	case OPLOCK_LEVEL_GRANULAR:
134 	default:
135 		smbOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
136 		break;
137 	}
138 
139 errout:
140 	sr->smb2_status = status;
141 	DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr);
142 	if (status) {
143 		smb2sr_put_error(sr, status);
144 		return (SDRC_SUCCESS);
145 	}
146 
147 	/*
148 	 * Encode an SMB2 Oplock Break Ack response
149 	 * [MS-SMB2] 2.2.25.1
150 	 */
151 	(void) smb_mbc_encodef(
152 	    &sr->reply, "wb5.qq",
153 	    SSZ_OPLOCK,			/* w */
154 	    smbOplockLevel,		/* b */
155 	    /* reserved			  5. */
156 	    smb2fid.persistent,		/* q */
157 	    smb2fid.temporal);		/* q */
158 
159 	return (SDRC_SUCCESS);
160 }
161 
162 /*
163  * Compose an SMB2 Oplock Break Notification packet, including
164  * the SMB2 header and everything, in sr->reply.
165  * The caller will send it and free the request.
166  */
167 void
168 smb2_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel)
169 {
170 	smb_ofile_t *ofile = sr->fid_ofile;
171 	smb2fid_t smb2fid;
172 	uint16_t StructSize;
173 	uint8_t OplockLevel;
174 
175 	/*
176 	 * Convert internal level to SMB2
177 	 */
178 	switch (NewLevel) {
179 	default:
180 		ASSERT(0);
181 		/* FALLTHROUGH */
182 	case OPLOCK_LEVEL_NONE:
183 		OplockLevel = SMB2_OPLOCK_LEVEL_NONE;
184 		break;
185 	case OPLOCK_LEVEL_TWO:
186 		OplockLevel = SMB2_OPLOCK_LEVEL_II;
187 		break;
188 	}
189 
190 	/*
191 	 * SMB2 Header
192 	 */
193 	sr->smb2_cmd_code = SMB2_OPLOCK_BREAK;
194 	sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR;
195 	sr->smb_tid = 0;
196 	sr->smb_pid = 0;
197 	sr->smb2_ssnid = 0;
198 	sr->smb2_messageid = UINT64_MAX;
199 	(void) smb2_encode_header(sr, B_FALSE);
200 
201 	/*
202 	 * SMB2 Oplock Break, variable part
203 	 */
204 	StructSize = 24;
205 	smb2fid.persistent = ofile->f_persistid;
206 	smb2fid.temporal = ofile->f_fid;
207 	(void) smb_mbc_encodef(
208 	    &sr->reply, "wb5.qq",
209 	    StructSize,		/* w */
210 	    OplockLevel,	/* b */
211 	    /* reserved		  5. */
212 	    smb2fid.persistent,	/* q */
213 	    smb2fid.temporal);	/* q */
214 }
215 
216 /*
217  * Client has an open handle and requests an oplock.
218  * Convert SMB2 oplock request info in to internal form,
219  * call common oplock code, convert result to SMB2.
220  *
221  * If necessary, "go async" here.
222  */
223 void
224 smb2_oplock_acquire(smb_request_t *sr)
225 {
226 	smb_arg_open_t *op = &sr->arg.open;
227 	smb_ofile_t *ofile = sr->fid_ofile;
228 	uint32_t status;
229 
230 	/* Only disk trees get oplocks. */
231 	ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE);
232 
233 	/* Only plain files... */
234 	if (!smb_node_is_file(ofile->f_node)) {
235 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
236 		return;
237 	}
238 
239 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
240 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
241 		return;
242 	}
243 
244 	/*
245 	 * SMB2: Convert to internal form.
246 	 */
247 	switch (op->op_oplock_level) {
248 	case SMB2_OPLOCK_LEVEL_BATCH:
249 		op->op_oplock_state = OPLOCK_LEVEL_BATCH;
250 		break;
251 	case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
252 		op->op_oplock_state = OPLOCK_LEVEL_ONE;
253 		break;
254 	case SMB2_OPLOCK_LEVEL_II:
255 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
256 		break;
257 	case SMB2_OPLOCK_LEVEL_LEASE:
258 		ASSERT(0); /* Handled elsewhere */
259 		/* FALLTHROUGH */
260 	case SMB2_OPLOCK_LEVEL_NONE:
261 	default:
262 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
263 		return;
264 	}
265 
266 	/*
267 	 * Tree options may force shared oplocks,
268 	 * in which case we reduce the request.
269 	 */
270 	if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
271 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
272 	}
273 
274 	/*
275 	 * Try exclusive first, if requested
276 	 */
277 	if ((op->op_oplock_state & BATCH_OR_EXCL) != 0) {
278 		status = smb_oplock_request(sr, ofile,
279 		    &op->op_oplock_state);
280 	} else {
281 		status = NT_STATUS_OPLOCK_NOT_GRANTED;
282 	}
283 
284 	/*
285 	 * If exclusive failed (or the tree forced shared oplocks)
286 	 * try for a shared oplock (Level II)
287 	 */
288 	if (status == NT_STATUS_OPLOCK_NOT_GRANTED) {
289 		op->op_oplock_state = OPLOCK_LEVEL_TWO;
290 		status = smb_oplock_request(sr, ofile,
291 		    &op->op_oplock_state);
292 	}
293 
294 	/*
295 	 * Either of the above may have returned the
296 	 * status code that says we should wait.
297 	 */
298 	if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
299 		(void) smb2sr_go_async(sr);
300 		(void) smb_oplock_wait_break(ofile->f_node, 0);
301 		status = 0;
302 	}
303 
304 	/*
305 	 * Keep track of what we got (in ofile->f_oplock.og_state)
306 	 * so we'll know what we had when sending a break later.
307 	 * The og_dialect here is the oplock dialect, not the
308 	 * SMB dialect.  No lease here, so SMB 2.0.
309 	 */
310 	ofile->f_oplock.og_dialect = SMB_VERS_2_002;
311 	switch (status) {
312 	case NT_STATUS_SUCCESS:
313 		ofile->f_oplock.og_state = op->op_oplock_state;
314 		break;
315 	case NT_STATUS_OPLOCK_NOT_GRANTED:
316 		ofile->f_oplock.og_state = 0;
317 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
318 		return;
319 	default:
320 		/* Caller did not check args sufficiently? */
321 		cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x",
322 		    sr->session->ip_addr_str, status);
323 		ofile->f_oplock.og_state = 0;
324 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
325 		return;
326 	}
327 
328 	/*
329 	 * Have STATUS_SUCCESS
330 	 * Convert internal oplock state to SMB2
331 	 */
332 	if (op->op_oplock_state & OPLOCK_LEVEL_GRANULAR) {
333 		ASSERT(0);
334 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
335 	} else if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
336 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
337 	} else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
338 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
339 	} else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
340 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
341 	} else {
342 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
343 	}
344 }
345 
346 /*
347  * smb2_oplock_reconnect()  Helper for smb2_dh_reconnect
348  * Get oplock state into op->op_oplock_level etc.
349  *
350  * Similar to the end of smb2_lease_acquire (for leases) or
351  * the end of smb2_oplock_acquire (for old-style oplocks).
352  */
353 void
354 smb2_oplock_reconnect(smb_request_t *sr)
355 {
356 	smb_arg_open_t *op = &sr->arg.open;
357 	smb_ofile_t *ofile = sr->fid_ofile;
358 
359 	op->op_oplock_state = ofile->f_oplock.og_state;
360 	if (ofile->f_lease != NULL) {
361 		smb_lease_t *ls = ofile->f_lease;
362 
363 		op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
364 		op->lease_state = ls->ls_state &
365 		    OPLOCK_LEVEL_CACHE_MASK;
366 		op->lease_flags = (ls->ls_breaking != 0) ?
367 		    SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0;
368 		op->lease_epoch = ls->ls_epoch;
369 		op->lease_version = ls->ls_version;
370 	} else {
371 		switch (op->op_oplock_state & OPLOCK_LEVEL_TYPE_MASK) {
372 		default:
373 		case OPLOCK_LEVEL_NONE:
374 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
375 			break;
376 		case OPLOCK_LEVEL_TWO:
377 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
378 			break;
379 		case OPLOCK_LEVEL_ONE:
380 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
381 			break;
382 		case OPLOCK_LEVEL_BATCH:
383 			op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
384 			break;
385 		}
386 	}
387 }
388