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 2020 Nexenta by DDN, Inc. All rights reserved.
14 * Copyright 2022 RackTop Systems, Inc.
15 */
16
17 /*
18 * (SMB1/SMB2) common (FS-level) Oplock support.
19 *
20 * This is the file-system (FS) level oplock code. This level
21 * knows about the rules by which various kinds of oplocks may
22 * coexist and how they interact. Note that this code should
23 * have NO knowledge of specific SMB protocol details. Those
24 * details are handled in smb_srv_oplock.c and related.
25 *
26 * This file is intentionally written to very closely follow the
27 * [MS-FSA] specification sections about oplocks. Almost every
28 * section of code is preceeded by a block of text from that
29 * specification describing the logic. Where the implementation
30 * differs from what the spec. describes, there are notes like:
31 * Implementation specific: ...
32 */
33
34 #include <smbsrv/smb_kproto.h>
35 #include <smbsrv/smb_oplock.h>
36
37 /*
38 * Several short-hand defines and enums used in this file.
39 */
40
41 #define NODE_FLAGS_DELETING (NODE_FLAGS_DELETE_ON_CLOSE |\
42 NODE_FLAGS_DELETE_COMMITTED)
43
44 static uint32_t
45 smb_oplock_req_excl(
46 smb_ofile_t *ofile, /* in: the "Open" */
47 uint32_t *rop); /* in: "RequestedOplock", out:NewOplockLevel */
48
49 static uint32_t
50 smb_oplock_req_shared(
51 smb_ofile_t *ofile, /* the "Open" */
52 uint32_t *rop, /* in: "RequestedOplock", out:NewOplockLevel */
53 boolean_t GrantingInAck);
54
55 static uint32_t smb_oplock_break_cmn(smb_node_t *node,
56 smb_ofile_t *ofile, uint32_t BreakCacheLevel);
57
58
59 /*
60 * [MS-FSA] 2.1.4.12.2 Algorithm to Compare Oplock Keys
61 *
62 * The inputs for this algorithm are:
63 *
64 * OperationOpen: The Open used in the request that can
65 * cause an oplock to break.
66 * OplockOpen: The Open originally used to request the oplock,
67 * as specified in section 2.1.5.17.
68 * Flags: If unspecified it is considered to contain 0.
69 * Valid nonzero values are:
70 * PARENT_OBJECT
71 *
72 * This algorithm returns TRUE if the appropriate oplock key field of
73 * OperationOpen equals OplockOpen.TargetOplockKey, and FALSE otherwise.
74 *
75 * Note: Unlike many comparison functions, ARG ORDER MATTERS.
76 */
77
78 static boolean_t
CompareOplockKeys(smb_ofile_t * OperOpen,smb_ofile_t * OplockOpen,int flags)79 CompareOplockKeys(smb_ofile_t *OperOpen, smb_ofile_t *OplockOpen, int flags)
80 {
81 static const uint8_t key0[SMB_LEASE_KEY_SZ] = { 0 };
82
83 /*
84 * When we're called via FEM, (smb_oplock_break_...)
85 * the OperOpen arg is NULL because I/O outside of SMB
86 * doesn't have an "ofile". That's "not a match".
87 */
88 if (OperOpen == NULL)
89 return (B_FALSE);
90 ASSERT(OplockOpen != NULL);
91
92 /*
93 * If OperationOpen equals OplockOpen:
94 * Return TRUE.
95 */
96 if (OperOpen == OplockOpen)
97 return (B_TRUE);
98
99 /*
100 * If both OperationOpen.TargetOplockKey and
101 * OperationOpen.ParentOplockKey are empty
102 * or both OplockOpen.TargetOplockKey and
103 * OplockOpen.ParentOplockKey are empty:
104 * Return FALSE.
105 */
106 if (bcmp(OperOpen->TargetOplockKey, key0, sizeof (key0)) == 0 &&
107 bcmp(OperOpen->ParentOplockKey, key0, sizeof (key0)) == 0)
108 return (B_FALSE);
109 if (bcmp(OplockOpen->TargetOplockKey, key0, sizeof (key0)) == 0 &&
110 bcmp(OplockOpen->ParentOplockKey, key0, sizeof (key0)) == 0)
111 return (B_FALSE);
112
113 /*
114 * If OplockOpen.TargetOplockKey is empty or...
115 */
116 if (bcmp(OplockOpen->TargetOplockKey, key0, sizeof (key0)) == 0)
117 return (B_FALSE);
118
119 /*
120 * If Flags contains PARENT_OBJECT:
121 */
122 if ((flags & PARENT_OBJECT) != 0) {
123 /*
124 * If OperationOpen.ParentOplockKey is empty:
125 * Return FALSE.
126 */
127 if (bcmp(OperOpen->ParentOplockKey, key0, sizeof (key0)) == 0)
128 return (B_FALSE);
129
130 /*
131 * If OperationOpen.ParentOplockKey equals
132 * OplockOpen.TargetOplockKey:
133 * return TRUE, else FALSE
134 */
135 if (bcmp(OperOpen->ParentOplockKey,
136 OplockOpen->TargetOplockKey,
137 SMB_LEASE_KEY_SZ) == 0) {
138 return (B_TRUE);
139 }
140 } else {
141 /*
142 * ... from above:
143 * (Flags does not contain PARENT_OBJECT and
144 * OperationOpen.TargetOplockKey is empty):
145 * Return FALSE.
146 */
147 if (bcmp(OperOpen->TargetOplockKey, key0, sizeof (key0)) == 0)
148 return (B_FALSE);
149
150 /*
151 * If OperationOpen.TargetOplockKey equals
152 * OplockOpen.TargetOplockKey:
153 * Return TRUE, else FALSE
154 */
155 if (bcmp(OperOpen->TargetOplockKey,
156 OplockOpen->TargetOplockKey,
157 SMB_LEASE_KEY_SZ) == 0) {
158 return (B_TRUE);
159 }
160 }
161
162 return (B_FALSE);
163 }
164
165 /*
166 * 2.1.4.13 Algorithm to Recompute the State of a Shared Oplock
167 *
168 * The inputs for this algorithm are:
169 * ThisOplock: The Oplock on whose state is being recomputed.
170 */
171 static void
RecomputeOplockState(smb_node_t * node)172 RecomputeOplockState(smb_node_t *node)
173 {
174 smb_oplock_t *ol = &node->n_oplock;
175
176 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
177 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
178
179 /*
180 * If ThisOplock.IIOplocks, ThisOplock.ROplocks, ThisOplock.RHOplocks,
181 * and ThisOplock.RHBreakQueue are all empty:
182 * Set ThisOplock.State to NO_OPLOCK.
183 */
184 if (ol->cnt_II == 0 && ol->cnt_R == 0 &&
185 ol->cnt_RH == 0 && ol->cnt_RHBQ == 0) {
186 ol->ol_state = NO_OPLOCK;
187 return;
188 }
189
190 /*
191 * Else If ThisOplock.ROplocks is not empty and either
192 * ThisOplock.RHOplocks or ThisOplock.RHBreakQueue are not empty:
193 * Set ThisOplock.State to
194 * (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH).
195 */
196 else if (ol->cnt_R != 0 && (ol->cnt_RH != 0 || ol->cnt_RHBQ != 0)) {
197 ol->ol_state = (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH);
198 }
199
200 /*
201 * Else If ThisOplock.ROplocks is empty and
202 * ThisOplock.RHOplocks is not empty:
203 * Set ThisOplock.State to (READ_CACHING|HANDLE_CACHING).
204 */
205 else if (ol->cnt_R == 0 && ol->cnt_RH != 0) {
206 ol->ol_state = (READ_CACHING|HANDLE_CACHING);
207 }
208
209 /*
210 * Else If ThisOplock.ROplocks is not empty and
211 * ThisOplock.IIOplocks is not empty:
212 * Set ThisOplock.State to (READ_CACHING|LEVEL_TWO_OPLOCK).
213 */
214 else if (ol->cnt_R != 0 && ol->cnt_II != 0) {
215 ol->ol_state = (READ_CACHING|LEVEL_TWO_OPLOCK);
216 }
217
218 /*
219 * Else If ThisOplock.ROplocks is not empty and
220 * ThisOplock.IIOplocks is empty:
221 * Set ThisOplock.State to READ_CACHING.
222 */
223 else if (ol->cnt_R != 0 && ol->cnt_II == 0) {
224 ol->ol_state = READ_CACHING;
225 }
226
227 /*
228 * Else If ThisOplock.ROplocks is empty and
229 * ThisOplock.IIOplocks is not empty:
230 * Set ThisOplock.State to LEVEL_TWO_OPLOCK.
231 */
232 else if (ol->cnt_R == 0 && ol->cnt_II != 0) {
233 ol->ol_state = LEVEL_TWO_OPLOCK;
234 }
235
236 else {
237 smb_ofile_t *o;
238 int cntBrkToRead;
239
240 /*
241 * ThisOplock.RHBreakQueue MUST be non-empty by this point.
242 */
243 ASSERT(ol->cnt_RHBQ != 0);
244
245 /*
246 * How many on RHBQ have BreakingToRead set?
247 */
248 cntBrkToRead = 0;
249 FOREACH_NODE_OFILE(node, o) {
250 if (o->f_oplock.onlist_RHBQ == 0)
251 continue;
252 if (o->f_oplock.BreakingToRead)
253 cntBrkToRead++;
254 }
255
256 /*
257 * If RHOpContext.BreakingToRead is TRUE for
258 * every RHOpContext on ThisOplock.RHBreakQueue:
259 */
260 if (cntBrkToRead == ol->cnt_RHBQ) {
261 /*
262 * Set ThisOplock.State to
263 * (READ_CACHING|HANDLE_CACHING|BREAK_TO_READ_CACHING).
264 */
265 ol->ol_state = (READ_CACHING|HANDLE_CACHING|
266 BREAK_TO_READ_CACHING);
267 }
268
269 /*
270 * Else If RHOpContext.BreakingToRead is FALSE for
271 * every RHOpContext on ThisOplock.RHBreakQueue:
272 */
273 else if (cntBrkToRead == 0) {
274 /*
275 * Set ThisOplock.State to
276 * (READ_CACHING|HANDLE_CACHING|BREAK_TO_NO_CACHING).
277 */
278 ol->ol_state = (READ_CACHING|HANDLE_CACHING|
279 BREAK_TO_NO_CACHING);
280 } else {
281 /*
282 * Set ThisOplock.State to
283 * (READ_CACHING|HANDLE_CACHING).
284 */
285 ol->ol_state = (READ_CACHING|HANDLE_CACHING);
286 }
287 }
288 }
289
290 /*
291 * [MS-FSA] 2.1.5.17 Server Requests an Oplock
292 *
293 * The server (caller) provides:
294 * Open - The Open on which the oplock is being requested. (ofile)
295 * Type - The type of oplock being requested. Valid values are as follows:
296 * LEVEL_TWO (Corresponds to SMB2_OPLOCK_LEVEL_II)
297 * LEVEL_ONE (Corresponds to SMB2_OPLOCK_LEVEL_EXCLUSIVE)
298 * LEVEL_BATCH (Corresponds to SMB2_OPLOCK_LEVEL_BATCH)
299 * LEVEL_GRANULAR (Corresponds to SMB2_OPLOCK_LEVEL_LEASE)
300 * RequestedOplockLevel - A combination of zero or more of the
301 * following flags (ignored if Type != LEVEL_GRANULAR)
302 * READ_CACHING
303 * HANDLE_CACHING
304 * WRITE_CACHING
305 *
306 * (Type + RequestedOplockLevel come in *statep)
307 *
308 * Returns:
309 * *statep = NewOplockLevel (possibly less than requested)
310 * containing: LEVEL_NONE, LEVEL_TWO + cache_flags
311 * NTSTATUS
312 */
313
314 uint32_t
smb_oplock_request(smb_request_t * sr,smb_ofile_t * ofile,uint32_t * statep)315 smb_oplock_request(smb_request_t *sr, smb_ofile_t *ofile, uint32_t *statep)
316 {
317 smb_node_t *node = ofile->f_node;
318 uint32_t status;
319
320 smb_llist_enter(&node->n_ofile_list, RW_READER);
321 mutex_enter(&node->n_oplock.ol_mutex);
322
323 status = smb_oplock_request_LH(sr, ofile, statep);
324
325 mutex_exit(&node->n_oplock.ol_mutex);
326 smb_llist_exit(&node->n_ofile_list);
327
328 return (status);
329 }
330
331 uint32_t
smb_oplock_request_LH(smb_request_t * sr,smb_ofile_t * ofile,uint32_t * statep)332 smb_oplock_request_LH(smb_request_t *sr, smb_ofile_t *ofile, uint32_t *statep)
333 {
334 smb_node_t *node = ofile->f_node;
335 uint32_t type = *statep & OPLOCK_LEVEL_TYPE_MASK;
336 uint32_t level = *statep & OPLOCK_LEVEL_CACHE_MASK;
337 uint32_t status;
338
339 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
340 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
341
342 *statep = LEVEL_NONE;
343
344 /*
345 * If Open.Stream.StreamType is DirectoryStream:
346 * The operation MUST be failed with STATUS_INVALID_PARAMETER
347 * under either of the following conditions:
348 * * Type is not LEVEL_GRANULAR.
349 * * Type is LEVEL_GRANULAR but RequestedOplockLevel is
350 * neither READ_CACHING nor (READ_CACHING|HANDLE_CACHING).
351 */
352 if (!smb_node_is_file(node)) {
353 /* ofile is a directory. */
354 if (type != LEVEL_GRANULAR)
355 return (NT_STATUS_INVALID_PARAMETER);
356 if (level != READ_CACHING &&
357 level != (READ_CACHING|HANDLE_CACHING))
358 return (NT_STATUS_INVALID_PARAMETER);
359 /*
360 * We're not supporting directory leases yet.
361 * Todo.
362 */
363 return (NT_STATUS_OPLOCK_NOT_GRANTED);
364 }
365
366 /*
367 * If Type is LEVEL_ONE or LEVEL_BATCH:
368 * The operation MUST be failed with STATUS_OPLOCK_NOT_GRANTED
369 * under either of the following conditions:
370 * Open.File.OpenList contains more than one Open
371 * whose Stream is the same as Open.Stream.
372 * Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or
373 * FILE_SYNCHRONOUS_IO_NONALERT.
374 * Request an exclusive oplock according to the algorithm in
375 * section 2.1.5.17.1, setting the algorithm's params as follows:
376 * Pass in the current Open.
377 * RequestedOplock = Type.
378 * The operation MUST at this point return any status code
379 * returned by the exclusive oplock request algorithm.
380 */
381 if (type == LEVEL_ONE || type == LEVEL_BATCH) {
382 if (node->n_open_count > 1) {
383 status = NT_STATUS_OPLOCK_NOT_GRANTED;
384 goto out;
385 }
386 /* XXX: Should be a flag on the ofile. */
387 if (node->flags & NODE_FLAGS_WRITE_THROUGH) {
388 status = NT_STATUS_OPLOCK_NOT_GRANTED;
389 goto out;
390 }
391 *statep = type;
392 status = smb_oplock_req_excl(ofile, statep);
393 goto out;
394 }
395
396 /*
397 * Else If Type is LEVEL_TWO:
398 * The operation MUST be failed with STATUS_OPLOCK_NOT_GRANTED under
399 * either of the following conditions:
400 * Open.Stream.ByteRangeLockList is not empty.
401 * Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or
402 * FILE_SYNCHRONOUS_IO_NONALERT.
403 * Request a shared oplock according to the algorithm in
404 * section 2.1.5.17.2, setting the algorithm's parameters as follows:
405 * Pass in the current Open.
406 * RequestedOplock = Type.
407 * GrantingInAck = FALSE.
408 * The operation MUST at this point return any status code
409 * returned by the shared oplock request algorithm.
410 */
411 if (type == LEVEL_TWO) {
412 if (smb_lock_range_access(sr, node, 0, ~0, B_FALSE) != 0) {
413 status = NT_STATUS_OPLOCK_NOT_GRANTED;
414 goto out;
415 }
416 /* XXX: Should be a flag on the ofile. */
417 if (node->flags & NODE_FLAGS_WRITE_THROUGH) {
418 status = NT_STATUS_OPLOCK_NOT_GRANTED;
419 goto out;
420 }
421 *statep = type;
422 status = smb_oplock_req_shared(ofile, statep, B_FALSE);
423 goto out;
424 }
425
426 /*
427 * Else If Type is LEVEL_GRANULAR:
428 * Sub-cases on RequestedOplockLevel (our "level")
429 *
430 * This is the last Type, so error on !granular and then
431 * deal with the cache levels using one less indent.
432 */
433 if (type != LEVEL_GRANULAR) {
434 status = NT_STATUS_INVALID_PARAMETER;
435 goto out;
436 }
437
438 switch (level) {
439
440 /*
441 * If RequestedOplockLevel is READ_CACHING or
442 * (READ_CACHING|HANDLE_CACHING):
443 * The operation MUST be failed with STATUS_OPLOCK_NOT_GRANTED
444 * under either of the following conditions:
445 * Open.Stream.ByteRangeLockList is not empty.
446 * Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or
447 * FILE_SYNCHRONOUS_IO_NONALERT.
448 * Request a shared oplock according to the algorithm in
449 * section 2.1.5.17.2, setting the parameters as follows:
450 * Pass in the current Open.
451 * RequestedOplock = RequestedOplockLevel.
452 * GrantingInAck = FALSE.
453 *
454 * The operation MUST at this point return any status code
455 * returned by the shared oplock request algorithm.
456 */
457 case READ_CACHING:
458 case (READ_CACHING|HANDLE_CACHING):
459 if (smb_lock_range_access(sr, node, 0, ~0, B_FALSE) != 0) {
460 status = NT_STATUS_OPLOCK_NOT_GRANTED;
461 goto out;
462 }
463 /* XXX: Should be a flag on the ofile. */
464 if (node->flags & NODE_FLAGS_WRITE_THROUGH) {
465 status = NT_STATUS_OPLOCK_NOT_GRANTED;
466 goto out;
467 }
468 *statep = level;
469 status = smb_oplock_req_shared(ofile, statep, B_FALSE);
470 break;
471
472 /*
473 * Else If RequestedOplockLevel is
474 * (READ_CACHING|WRITE_CACHING) or
475 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING):
476 * If Open.Mode contains either FILE_SYNCHRONOUS_IO_ALERT or
477 * FILE_SYNCHRONOUS_IO_NONALERT, the operation MUST be failed
478 * with STATUS_OPLOCK_NOT_GRANTED.
479 * Request an exclusive oplock according to the algorithm in
480 * section 2.1.5.17.1, setting the parameters as follows:
481 * Pass in the current Open.
482 * RequestedOplock = RequestedOplockLevel.
483 * The operation MUST at this point return any status code
484 * returned by the exclusive oplock request algorithm.
485 */
486 case (READ_CACHING | WRITE_CACHING):
487 case (READ_CACHING | WRITE_CACHING | HANDLE_CACHING):
488 /* XXX: Should be a flag on the ofile. */
489 if (node->flags & NODE_FLAGS_WRITE_THROUGH) {
490 status = NT_STATUS_OPLOCK_NOT_GRANTED;
491 goto out;
492 }
493 *statep = level;
494 status = smb_oplock_req_excl(ofile, statep);
495 break;
496
497 /*
498 * Else if RequestedOplockLevel is 0 (that is, no flags):
499 * The operation MUST return STATUS_SUCCESS at this point.
500 */
501 case 0:
502 *statep = 0;
503 status = NT_STATUS_SUCCESS;
504 break;
505
506 /*
507 * Else
508 * The operation MUST be failed with STATUS_INVALID_PARAMETER.
509 */
510 default:
511 status = NT_STATUS_INVALID_PARAMETER;
512 break;
513 }
514
515 /*
516 * Give caller back the "Granular" bit, eg. when
517 * NT_STATUS_SUCCESS or NT_STATUS_OPLOCK_BREAK_IN_PROGRESS
518 */
519 if (NT_SC_SEVERITY(status) == NT_STATUS_SEVERITY_SUCCESS) {
520 *statep |= LEVEL_GRANULAR;
521 }
522
523 out:
524 return (status);
525 }
526
527 /*
528 * 2.1.5.17.1 Algorithm to Request an Exclusive Oplock
529 *
530 * The inputs for requesting an exclusive oplock are:
531 * Open: The Open on which the oplock is being requested.
532 * RequestedOplock: The oplock type being requested. One of:
533 * LEVEL_ONE, LEVEL_BATCH, CACHE_RW, CACHE_RWH
534 *
535 * On completion, the object store MUST return:
536 * Status: An NTSTATUS code that specifies the result.
537 * NewOplockLevel: The type of oplock that the requested oplock has been
538 * broken (reduced) to. If a failure status is returned in Status,
539 * the value of this field is undefined. Valid values are as follows:
540 * LEVEL_NONE (that is, no oplock)
541 * LEVEL_TWO
542 * A combination of one or more of the following flags:
543 * READ_CACHING
544 * HANDLE_CACHING
545 * WRITE_CACHING
546 * AcknowledgeRequired: A Boolean value: TRUE if the server MUST
547 * acknowledge the oplock break; FALSE if not, as specified in
548 * section 2.1.5.18. If a failure status is returned in Status,
549 * the value of this field is undefined.
550 *
551 * Note: Stores NewOplockLevel in *rop
552 */
553 static uint32_t
smb_oplock_req_excl(smb_ofile_t * ofile,uint32_t * rop)554 smb_oplock_req_excl(
555 smb_ofile_t *ofile, /* in: the "Open" */
556 uint32_t *rop) /* in: "RequestedOplock", out:NewOplockLevel */
557 {
558 smb_node_t *node = ofile->f_node;
559 smb_ofile_t *o;
560 boolean_t GrantExcl = B_FALSE;
561 uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED;
562
563 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
564 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
565
566 #ifdef DEBUG
567 FOREACH_NODE_OFILE(node, o) {
568 DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
569 }
570 #endif
571
572 /*
573 * Don't allow grants on closing ofiles.
574 */
575 if (ofile->f_oplock_closing)
576 return (status);
577
578 /*
579 * If Open.Stream.Oplock is empty:
580 * Build a new Oplock object with fields initialized as follows:
581 * Oplock.State set to NO_OPLOCK.
582 * All other fields set to 0/empty.
583 * Store the new Oplock object in Open.Stream.Oplock.
584 * EndIf
585 *
586 * Implementation specific:
587 * Open.Stream.Oplock maps to: node->n_oplock
588 */
589 if (node->n_oplock.ol_state == 0) {
590 node->n_oplock.ol_state = NO_OPLOCK;
591 }
592
593 /*
594 * If Open.Stream.Oplock.State contains
595 * LEVEL_TWO_OPLOCK or NO_OPLOCK: ...
596 *
597 * Per ms, this is the "If" matching the unbalalanced
598 * "Else If" below (for which we requested clarification).
599 */
600 if ((node->n_oplock.ol_state & (LEVEL_TWO | NO_OPLOCK)) != 0) {
601
602 /*
603 * If Open.Stream.Oplock.State contains LEVEL_TWO_OPLOCK and
604 * RequestedOplock contains one or more of READ_CACHING,
605 * HANDLE_CACHING, or WRITE_CACHING, the operation MUST be
606 * failed with Status set to STATUS_OPLOCK_NOT_GRANTED.
607 */
608 if ((node->n_oplock.ol_state & LEVEL_TWO) != 0 &&
609 (*rop & CACHE_RWH) != 0) {
610 status = NT_STATUS_OPLOCK_NOT_GRANTED;
611 goto out;
612 }
613
614 /*
615 * [ from dochelp@ms ]
616 *
617 * By this point if there is a level II oplock present,
618 * the caller can only be requesting an old-style oplock
619 * because we rejected enhanced oplock requests above.
620 * If the caller is requesting an old-style oplock our
621 * caller already verfied that there is only one handle
622 * open to this stream, and we've already verified that
623 * this request is for a legacy oplock, meaning that there
624 * can be at most one level II oplock (and no R oplocks),
625 * and the level II oplock belongs to this handle. Clear
626 * the level II oplock and grant the exclusive oplock.
627 */
628
629 /*
630 * If Open.Stream.Oplock.State is equal to LEVEL_TWO_OPLOCK:
631 * Remove the first Open ThisOpen from
632 * Open.Stream.Oplock.IIOplocks (there is supposed to be
633 * exactly one present), and notify the server of an
634 * oplock break according to the algorithm in section
635 * 2.1.5.17.3, setting the algorithm's parameters as follows:
636 * BreakingOplockOpen = ThisOpen.
637 * NewOplockLevel = LEVEL_NONE.
638 * AcknowledgeRequired = FALSE.
639 * OplockCompletionStatus = STATUS_SUCCESS.
640 * (The operation does not end at this point; this call
641 * to 2.1.5.17.3 completes some earlier call to 2.1.5.17.2.)
642 *
643 * Implementation specific:
644 *
645 * As explained above, the passed in ofile should be the
646 * only open file on this node. Out of caution, we'll
647 * walk the ofile list as usual here, making sure there
648 * are no LevelII oplocks remaining, as those may not
649 * coexist with the exclusive oplock were're creating
650 * in this call. Also, if the passed in ofile has a
651 * LevelII oplock, don't do an "ind break" up call on
652 * this ofile, as that would just cause an immediate
653 * "break to none" of the oplock we'll grant here.
654 * If there were other ofiles with LevelII oplocks,
655 * it would be appropriate to "ind break" those.
656 */
657 if ((node->n_oplock.ol_state & LEVEL_TWO) != 0) {
658 FOREACH_NODE_OFILE(node, o) {
659 if (o->f_oplock.onlist_II == 0)
660 continue;
661 o->f_oplock.onlist_II = B_FALSE;
662 node->n_oplock.cnt_II--;
663 ASSERT(node->n_oplock.cnt_II >= 0);
664 if (o == ofile)
665 continue;
666 DTRACE_PROBE1(unexpected, smb_ofile_t *, o);
667 smb_oplock_ind_break(o,
668 LEVEL_NONE, B_FALSE,
669 NT_STATUS_SUCCESS);
670 }
671 }
672
673 /*
674 * Note the spec. had an extra "EndIf" here.
675 * Confirmed by dochelp@ms
676 */
677
678 /*
679 * If Open.File.OpenList contains more than one Open whose
680 * Stream is the same as Open.Stream, and NO_OPLOCK is present
681 * in Open.Stream.Oplock.State, the operation MUST be failed
682 * with Status set to STATUS_OPLOCK_NOT_GRANTED.
683 *
684 * Implementation specific:
685 * Allow other opens if they have the same lease ours,
686 * so we can upgrade RH to RWH (for example). Therefore
687 * only count opens with a different TargetOplockKey.
688 * Also ignore "attribute-only" opens.
689 */
690 if ((node->n_oplock.ol_state & NO_OPLOCK) != 0) {
691 FOREACH_NODE_OFILE(node, o) {
692 if (!smb_ofile_is_open(o))
693 continue;
694 if ((o->f_granted_access & FILE_DATA_ALL) == 0)
695 continue;
696 if (!CompareOplockKeys(ofile, o, 0)) {
697 status = NT_STATUS_OPLOCK_NOT_GRANTED;
698 goto out;
699 }
700 }
701 }
702
703 /*
704 * If Open.Stream.IsDeleted is TRUE and RequestedOplock
705 * contains HANDLE_CACHING, the operation MUST be failed
706 * with Status set to STATUS_OPLOCK_NOT_GRANTED.
707 */
708 if (((node->flags & NODE_FLAGS_DELETING) != 0) &&
709 (*rop & HANDLE_CACHING) != 0) {
710 status = NT_STATUS_OPLOCK_NOT_GRANTED;
711 goto out;
712 }
713
714 /* Set GrantExclusiveOplock to TRUE. */
715 GrantExcl = B_TRUE;
716 }
717
718 /*
719 * "Else" If (Open.Stream.Oplock.State contains one or more of
720 * READ_CACHING, WRITE_CACHING, or HANDLE_CACHING) and
721 * (Open.Stream.Oplock.State contains none of (BREAK_ANY)) and
722 * (Open.Stream.Oplock.RHBreakQueue is empty):
723 */
724 else if ((node->n_oplock.ol_state & CACHE_RWH) != 0 &&
725 (node->n_oplock.ol_state & BREAK_ANY) == 0 &&
726 node->n_oplock.cnt_RHBQ == 0) {
727
728 /*
729 * This is a granular oplock and it is not breaking.
730 */
731
732 /*
733 * If RequestedOplock contains none of READ_CACHING,
734 * WRITE_CACHING, or HANDLE_CACHING, the operation
735 * MUST be failed with Status set to
736 * STATUS_OPLOCK_NOT_GRANTED.
737 */
738 if ((*rop & CACHE_RWH) == 0) {
739 status = NT_STATUS_OPLOCK_NOT_GRANTED;
740 goto out;
741 }
742
743 /*
744 * If Open.Stream.IsDeleted (already checked above)
745 */
746
747 /*
748 * Switch (Open.Stream.Oplock.State):
749 */
750 switch (node->n_oplock.ol_state) {
751
752 case CACHE_R:
753 /*
754 * If RequestedOplock is neither
755 * (READ_CACHING|WRITE_CACHING) nor
756 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING),
757 * the operation MUST be failed with Status set
758 * to STATUS_OPLOCK_NOT_GRANTED.
759 */
760 if (*rop != CACHE_RW && *rop != CACHE_RWH) {
761 status = NT_STATUS_OPLOCK_NOT_GRANTED;
762 goto out;
763 }
764
765 /*
766 * For each Open ThisOpen in
767 * Open.Stream.Oplock.ROplocks:
768 * If ThisOpen.TargetOplockKey !=
769 * Open.TargetOplockKey, the operation
770 * MUST be failed with Status set to
771 * STATUS_OPLOCK_NOT_GRANTED.
772 * EndFor
773 */
774 FOREACH_NODE_OFILE(node, o) {
775 if (o->f_oplock.onlist_R == 0)
776 continue;
777 if (!CompareOplockKeys(ofile, o, 0)) {
778 status = NT_STATUS_OPLOCK_NOT_GRANTED;
779 goto out;
780 }
781 }
782
783 /*
784 * For each Open o in Open.Stream.Oplock.ROplocks:
785 * Remove o from Open.Stream.Oplock.ROplocks.
786 * Notify the server of an oplock break
787 * according to the algorithm in section
788 * 2.1.5.17.3, setting the algorithm's
789 * parameters as follows:
790 * BreakingOplockOpen = o.
791 * NewOplockLevel = RequestedOplock.
792 * AcknowledgeRequired = FALSE.
793 * OplockCompletionStatus =
794 * STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
795 * (The operation does not end at this point;
796 * this call to 2.1.5.17.3 completes some
797 * earlier call to 2.1.5.17.2.)
798 * EndFor
799 *
800 * Note: Upgrade to excl. on same lease.
801 * Won't send a break for this.
802 */
803 FOREACH_NODE_OFILE(node, o) {
804 if (o->f_oplock.onlist_R == 0)
805 continue;
806 o->f_oplock.onlist_R = B_FALSE;
807 node->n_oplock.cnt_R--;
808 ASSERT(node->n_oplock.cnt_R >= 0);
809
810 smb_oplock_ind_break(o, *rop,
811 B_FALSE, STATUS_NEW_HANDLE);
812 }
813 /*
814 * Set GrantExclusiveOplock to TRUE.
815 * EndCase // _R
816 */
817 GrantExcl = B_TRUE;
818 break;
819
820 case CACHE_RH:
821 /*
822 * If RequestedOplock is not
823 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING)
824 * or Open.Stream.Oplock.RHBreakQueue is not empty,
825 * the operation MUST be failed with Status set to
826 * STATUS_OPLOCK_NOT_GRANTED.
827 * Note: Have RHBreakQueue==0 from above.
828 */
829 if (*rop != CACHE_RWH) {
830 status = NT_STATUS_OPLOCK_NOT_GRANTED;
831 goto out;
832 }
833
834 /*
835 * For each Open ThisOpen in
836 * Open.Stream.Oplock.RHOplocks:
837 * If ThisOpen.TargetOplockKey !=
838 * Open.TargetOplockKey, the operation
839 * MUST be failed with Status set to
840 * STATUS_OPLOCK_NOT_GRANTED.
841 * EndFor
842 */
843 FOREACH_NODE_OFILE(node, o) {
844 if (o->f_oplock.onlist_RH == 0)
845 continue;
846 if (!CompareOplockKeys(ofile, o, 0)) {
847 status = NT_STATUS_OPLOCK_NOT_GRANTED;
848 goto out;
849 }
850 }
851
852 /*
853 * For each Open o in Open.Stream.Oplock.RHOplocks:
854 * Remove o from Open.Stream.Oplock.RHOplocks.
855 * Notify the server of an oplock break
856 * according to the algorithm in section
857 * 2.1.5.17.3, setting the algorithm's
858 * parameters as follows:
859 * BreakingOplockOpen = o.
860 * NewOplockLevel = RequestedOplock.
861 * AcknowledgeRequired = FALSE.
862 * OplockCompletionStatus =
863 * STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
864 * (The operation does not end at this point;
865 * this call to 2.1.5.17.3 completes some
866 * earlier call to 2.1.5.17.2.)
867 * EndFor
868 *
869 * Note: Upgrade to excl. on same lease.
870 * Won't send a break for this.
871 */
872 FOREACH_NODE_OFILE(node, o) {
873 if (o->f_oplock.onlist_RH == 0)
874 continue;
875 o->f_oplock.onlist_RH = B_FALSE;
876 node->n_oplock.cnt_RH--;
877 ASSERT(node->n_oplock.cnt_RH >= 0);
878
879 smb_oplock_ind_break(o, *rop,
880 B_FALSE, STATUS_NEW_HANDLE);
881 }
882 /*
883 * Set GrantExclusiveOplock to TRUE.
884 * EndCase // _RH
885 */
886 GrantExcl = B_TRUE;
887 break;
888
889 case (CACHE_RWH | EXCLUSIVE):
890 /*
891 * If RequestedOplock is not
892 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING),
893 * the operation MUST be failed with Status set to
894 * STATUS_OPLOCK_NOT_GRANTED.
895 */
896 if (*rop != CACHE_RWH) {
897 status = NT_STATUS_OPLOCK_NOT_GRANTED;
898 goto out;
899 }
900 /* Deliberate FALL-THROUGH to next Case statement. */
901 /* FALLTHROUGH */
902
903 case (CACHE_RW | EXCLUSIVE):
904 /*
905 * If RequestedOplock is neither
906 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING) nor
907 * (READ_CACHING|WRITE_CACHING), the operation MUST be
908 * failed with Status set to STATUS_OPLOCK_NOT_GRANTED.
909 */
910 if (*rop != CACHE_RWH && *rop != CACHE_RW) {
911 status = NT_STATUS_OPLOCK_NOT_GRANTED;
912 goto out;
913 }
914
915 o = node->n_oplock.excl_open;
916 if (o == NULL) {
917 ASSERT(0);
918 GrantExcl = B_TRUE;
919 break;
920 }
921
922 /*
923 * If Open.TargetOplockKey !=
924 * Open.Stream.Oplock.ExclusiveOpen.TargetOplockKey,
925 * the operation MUST be failed with Status set to
926 * STATUS_OPLOCK_NOT_GRANTED.
927 */
928 if (!CompareOplockKeys(ofile, o, 0)) {
929 status = NT_STATUS_OPLOCK_NOT_GRANTED;
930 goto out;
931 }
932
933 /*
934 * Notify the server of an oplock break according to
935 * the algorithm in section 2.1.5.17.3, setting the
936 * algorithm's parameters as follows:
937 * BreakingOplockOpen =
938 * Open.Stream.Oplock.ExclusiveOpen.
939 * NewOplockLevel = RequestedOplock.
940 * AcknowledgeRequired = FALSE.
941 * OplockCompletionStatus =
942 * STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
943 * (The operation does not end at this point;
944 * this call to 2.1.5.17.3 completes some
945 * earlier call to 2.1.5.17.1.)
946 *
947 * Set Open.Stream.Oplock.ExclusiveOpen to NULL.
948 * Set GrantExclusiveOplock to TRUE.
949 *
950 * Note: We will keep this exclusive oplock,
951 * but move it to a new handle on this lease.
952 * Won't send a break for this.
953 */
954 smb_oplock_ind_break(o, *rop,
955 B_FALSE, STATUS_NEW_HANDLE);
956 node->n_oplock.excl_open = o = NULL;
957 GrantExcl = B_TRUE;
958 break;
959
960 default:
961 /*
962 * The operation MUST be failed with Status set to
963 * STATUS_OPLOCK_NOT_GRANTED.
964 */
965 status = NT_STATUS_OPLOCK_NOT_GRANTED;
966 goto out;
967
968 } /* switch n_oplock.ol_state */
969 } /* EndIf CACHE_RWH & !BREAK_ANY... */
970 else {
971 /*
972 * The operation MUST be failed with...
973 */
974 status = NT_STATUS_OPLOCK_NOT_GRANTED;
975 goto out;
976 }
977
978 /*
979 * If GrantExclusiveOplock is TRUE:
980 *
981 * Set Open.Stream.Oplock.ExclusiveOpen = Open.
982 * Set Open.Stream.Oplock.State =
983 * (RequestedOplock|EXCLUSIVE).
984 */
985 if (GrantExcl) {
986 node->n_oplock.excl_open = ofile;
987 node->n_oplock.ol_state = *rop | EXCLUSIVE;
988
989 /*
990 * This operation MUST be made cancelable...
991 * This operation waits until the oplock is
992 * broken or canceled, as specified in
993 * section 2.1.5.17.3. Note: This function
994 * does not cause breaks that require a wait,
995 * so never returns ..._BREAK_IN_PROGRESS.
996 *
997 * When the operation specified in section
998 * 2.1.5.17.3 is called, its following input
999 * parameters are transferred to this routine
1000 * and then returned by it:
1001 *
1002 * Status is set to OplockCompletionStatus
1003 * NewOplockLevel, AcknowledgeRequired...
1004 * from the operation specified in
1005 * section 2.1.5.17.3.
1006 */
1007 /* Keep *rop = ... from caller. */
1008 status = NT_STATUS_SUCCESS;
1009
1010 /*
1011 * First oplock grant installs FEM hooks.
1012 */
1013 if (node->n_oplock.ol_fem == B_FALSE) {
1014 if (smb_fem_oplock_install(node) != 0) {
1015 cmn_err(CE_NOTE,
1016 "smb_fem_oplock_install failed");
1017 } else {
1018 node->n_oplock.ol_fem = B_TRUE;
1019 }
1020 }
1021 }
1022
1023 out:
1024 if (status == NT_STATUS_OPLOCK_NOT_GRANTED)
1025 *rop = LEVEL_NONE;
1026
1027 return (status);
1028 }
1029
1030 /*
1031 * 2.1.5.17.2 Algorithm to Request a Shared Oplock
1032 *
1033 * The inputs for requesting a shared oplock are:
1034 * Open: The Open on which the oplock is being requested.
1035 * RequestedOplock: The oplock type being requested.
1036 * GrantingInAck: A Boolean value, TRUE if this oplock is being
1037 * requested as part of an oplock break acknowledgement,
1038 * FALSE if not.
1039 *
1040 * On completion, the object store MUST return:
1041 * Status: An NTSTATUS code that specifies the result.
1042 * NewOplockLevel: The type of oplock that the requested oplock has been
1043 * broken (reduced) to. If a failure status is returned in Status,
1044 * the value of this field is undefined. Valid values are as follows:
1045 * LEVEL_NONE (that is, no oplock)
1046 * LEVEL_TWO
1047 * A combination of one or more of the following flags:
1048 * READ_CACHING
1049 * HANDLE_CACHING
1050 * WRITE_CACHING
1051 * AcknowledgeRequired: A Boolean value: TRUE if the server MUST
1052 * acknowledge the oplock break; FALSE if not, as specified in
1053 * section 2.1.5.18. If a failure status is returned in Status,
1054 * the value of this field is undefined.
1055 *
1056 * Note: Stores NewOplockLevel in *rop
1057 */
1058 static uint32_t
smb_oplock_req_shared(smb_ofile_t * ofile,uint32_t * rop,boolean_t GrantingInAck)1059 smb_oplock_req_shared(
1060 smb_ofile_t *ofile, /* in: the "Open" */
1061 uint32_t *rop, /* in: "RequestedOplock", out:NewOplockLevel */
1062 boolean_t GrantingInAck)
1063 {
1064 smb_node_t *node = ofile->f_node;
1065 smb_ofile_t *o;
1066 boolean_t OplockGranted = B_FALSE;
1067 uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED;
1068
1069 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
1070 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
1071
1072 #ifdef DEBUG
1073 FOREACH_NODE_OFILE(node, o) {
1074 DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
1075 }
1076 #endif
1077
1078 /*
1079 * Don't allow grants on closing ofiles.
1080 */
1081 if (ofile->f_oplock_closing)
1082 return (status);
1083
1084 /*
1085 * If Open.Stream.Oplock is empty:
1086 * Build a new Oplock object with fields initialized as follows:
1087 * Oplock.State set to NO_OPLOCK.
1088 * All other fields set to 0/empty.
1089 * Store the new Oplock object in Open.Stream.Oplock.
1090 * EndIf
1091 *
1092 * Implementation specific:
1093 * Open.Stream.Oplock maps to: node->n_oplock
1094 */
1095 if (node->n_oplock.ol_state == 0) {
1096 node->n_oplock.ol_state = NO_OPLOCK;
1097 }
1098
1099 /*
1100 * If (GrantingInAck is FALSE) and (Open.Stream.Oplock.State
1101 * contains one or more of BREAK_TO_TWO, BREAK_TO_NONE,
1102 * BREAK_TO_TWO_TO_NONE, BREAK_TO_READ_CACHING,
1103 * BREAK_TO_WRITE_CACHING, BREAK_TO_HANDLE_CACHING,
1104 * BREAK_TO_NO_CACHING, or EXCLUSIVE), then:
1105 * The operation MUST be failed with Status set to
1106 * STATUS_OPLOCK_NOT_GRANTED.
1107 * EndIf
1108 */
1109 if (GrantingInAck == B_FALSE &&
1110 (node->n_oplock.ol_state & (BREAK_ANY | EXCLUSIVE)) != 0) {
1111 status = NT_STATUS_OPLOCK_NOT_GRANTED;
1112 goto out;
1113 }
1114
1115 /* Switch (RequestedOplock): */
1116 switch (*rop) {
1117
1118 case LEVEL_TWO:
1119 /*
1120 * The operation MUST be failed with Status set to
1121 * STATUS_OPLOCK_NOT_GRANTED if Open.Stream.Oplock.State
1122 * is anything other than the following:
1123 * NO_OPLOCK
1124 * LEVEL_TWO_OPLOCK
1125 * READ_CACHING
1126 * (LEVEL_TWO_OPLOCK|READ_CACHING)
1127 */
1128 switch (node->n_oplock.ol_state) {
1129 default:
1130 status = NT_STATUS_OPLOCK_NOT_GRANTED;
1131 goto out;
1132 case NO_OPLOCK:
1133 case LEVEL_TWO:
1134 case READ_CACHING:
1135 case (LEVEL_TWO | READ_CACHING):
1136 break;
1137 }
1138 /* Deliberate FALL-THROUGH to next Case statement. */
1139 /* FALLTHROUGH */
1140
1141 case READ_CACHING:
1142 /*
1143 * The operation MUST be failed with Status set to
1144 * STATUS_OPLOCK_NOT_GRANTED if GrantingInAck is FALSE
1145 * and Open.Stream.Oplock.State is anything other than...
1146 */
1147 switch (node->n_oplock.ol_state) {
1148 default:
1149 if (GrantingInAck == B_FALSE) {
1150 status = NT_STATUS_OPLOCK_NOT_GRANTED;
1151 goto out;
1152 }
1153 break;
1154 case NO_OPLOCK:
1155 case LEVEL_TWO:
1156 case READ_CACHING:
1157 case (LEVEL_TWO | READ_CACHING):
1158 case (READ_CACHING | HANDLE_CACHING):
1159 case (READ_CACHING | HANDLE_CACHING | MIXED_R_AND_RH):
1160 case (READ_CACHING | HANDLE_CACHING | BREAK_TO_READ_CACHING):
1161 case (READ_CACHING | HANDLE_CACHING | BREAK_TO_NO_CACHING):
1162 break;
1163 }
1164
1165 if (GrantingInAck == B_FALSE) {
1166 /*
1167 * If there is an Open on
1168 * Open.Stream.Oplock.RHOplocks
1169 * whose TargetOplockKey is equal to
1170 * Open.TargetOplockKey, the operation
1171 * MUST be failed with Status set to
1172 * STATUS_OPLOCK_NOT_GRANTED.
1173 *
1174 * If there is an Open on
1175 * Open.Stream.Oplock.RHBreakQueue
1176 * whose TargetOplockKey is equal to
1177 * Open.TargetOplockKey, the operation
1178 * MUST be failed with Status set to
1179 * STATUS_OPLOCK_NOT_GRANTED.
1180 *
1181 * Implement both in one list walk.
1182 */
1183 FOREACH_NODE_OFILE(node, o) {
1184 if ((o->f_oplock.onlist_RH ||
1185 o->f_oplock.onlist_RHBQ) &&
1186 CompareOplockKeys(ofile, o, 0)) {
1187 status = NT_STATUS_OPLOCK_NOT_GRANTED;
1188 goto out;
1189 }
1190 }
1191
1192 /*
1193 * If there is an Open ThisOpen on
1194 * Open.Stream.Oplock.ROplocks whose
1195 * TargetOplockKey is equal to Open.TargetOplockKey
1196 * (there is supposed to be at most one present):
1197 * * Remove ThisOpen from Open...ROplocks.
1198 * * Notify the server of an oplock break
1199 * according to the algorithm in section
1200 * 2.1.5.17.3, setting the algorithm's
1201 * parameters as follows:
1202 * * BreakingOplockOpen = ThisOpen
1203 * * NewOplockLevel = READ_CACHING
1204 * * AcknowledgeRequired = FALSE
1205 * * OplockCompletionStatus =
1206 * STATUS_..._NEW_HANDLE
1207 * (The operation does not end at this point;
1208 * this call to 2.1.5.17.3 completes some
1209 * earlier call to 2.1.5.17.2.)
1210 * EndIf
1211 *
1212 * If this SMB2 lease already has an "R" handle,
1213 * we'll update that lease locally to point to
1214 * this new handle.
1215 */
1216 FOREACH_NODE_OFILE(node, o) {
1217 if (o->f_oplock.onlist_R == 0)
1218 continue;
1219 if (CompareOplockKeys(ofile, o, 0)) {
1220 o->f_oplock.onlist_R = B_FALSE;
1221 node->n_oplock.cnt_R--;
1222 ASSERT(node->n_oplock.cnt_R >= 0);
1223 smb_oplock_ind_break(o,
1224 CACHE_R, B_FALSE,
1225 STATUS_NEW_HANDLE);
1226 }
1227 }
1228 } /* EndIf !GrantingInAck */
1229
1230 /*
1231 * If RequestedOplock equals LEVEL_TWO:
1232 * Add Open to Open.Stream.Oplock.IIOplocks.
1233 * Else // RequestedOplock equals READ_CACHING:
1234 * Add Open to Open.Stream.Oplock.ROplocks.
1235 * EndIf
1236 */
1237 if (*rop == LEVEL_TWO) {
1238 ofile->f_oplock.onlist_II = B_TRUE;
1239 node->n_oplock.cnt_II++;
1240 } else {
1241 /* (*rop == READ_CACHING) */
1242 if (ofile->f_oplock.onlist_R == B_FALSE) {
1243 ofile->f_oplock.onlist_R = B_TRUE;
1244 node->n_oplock.cnt_R++;
1245 }
1246 }
1247
1248 /*
1249 * Recompute Open.Stream.Oplock.State according to the
1250 * algorithm in section 2.1.4.13, passing Open.Stream.Oplock
1251 * as the ThisOplock parameter.
1252 * Set OplockGranted to TRUE.
1253 */
1254 RecomputeOplockState(node);
1255 OplockGranted = B_TRUE;
1256 break;
1257
1258 case (READ_CACHING|HANDLE_CACHING):
1259 /*
1260 * The operation MUST be failed with Status set to
1261 * STATUS_OPLOCK_NOT_GRANTED if GrantingInAck is FALSE
1262 * and Open.Stream.Oplock.State is anything other than...
1263 */
1264 switch (node->n_oplock.ol_state) {
1265 default:
1266 if (GrantingInAck == B_FALSE) {
1267 status = NT_STATUS_OPLOCK_NOT_GRANTED;
1268 goto out;
1269 }
1270 break;
1271 case NO_OPLOCK:
1272 case READ_CACHING:
1273 case (READ_CACHING | HANDLE_CACHING):
1274 case (READ_CACHING | HANDLE_CACHING | MIXED_R_AND_RH):
1275 case (READ_CACHING | HANDLE_CACHING | BREAK_TO_READ_CACHING):
1276 case (READ_CACHING | HANDLE_CACHING | BREAK_TO_NO_CACHING):
1277 break;
1278 }
1279
1280 /*
1281 * If Open.Stream.IsDeleted is TRUE, the operation MUST be
1282 * failed with Status set to STATUS_OPLOCK_NOT_GRANTED.
1283 */
1284 if ((node->flags & NODE_FLAGS_DELETING) != 0) {
1285 status = NT_STATUS_OPLOCK_NOT_GRANTED;
1286 goto out;
1287 }
1288
1289 if (GrantingInAck == B_FALSE) {
1290 /*
1291 * If there is an Open ThisOpen on
1292 * Open.Stream.Oplock.ROplocks whose
1293 * TargetOplockKey is equal to Open.TargetOplockKey
1294 * (there is supposed to be at most one present):
1295 * * Remove ThisOpen from Open...ROplocks.
1296 * * Notify the server of an oplock break
1297 * according to the algorithm in section
1298 * 2.1.5.17.3, setting the algorithm's
1299 * parameters as follows:
1300 * * BreakingOplockOpen = ThisOpen
1301 * * NewOplockLevel = CACHE_RH
1302 * * AcknowledgeRequired = FALSE
1303 * * OplockCompletionStatus =
1304 * STATUS_..._NEW_HANDLE
1305 * (The operation does not end at this point;
1306 * this call to 2.1.5.17.3 completes some
1307 * earlier call to 2.1.5.17.2.)
1308 * EndIf
1309 *
1310 * If this SMB2 lease already has an "R" handle,
1311 * we'll update that lease locally to point to
1312 * this new handle (upgrade to "RH").
1313 */
1314 FOREACH_NODE_OFILE(node, o) {
1315 if (o->f_oplock.onlist_R == 0)
1316 continue;
1317 if (CompareOplockKeys(ofile, o, 0)) {
1318 o->f_oplock.onlist_R = B_FALSE;
1319 node->n_oplock.cnt_R--;
1320 ASSERT(node->n_oplock.cnt_R >= 0);
1321 smb_oplock_ind_break(o,
1322 CACHE_RH, B_FALSE,
1323 STATUS_NEW_HANDLE);
1324 }
1325 }
1326
1327 /*
1328 * If there is an Open ThisOpen on
1329 * Open.Stream.Oplock.RHOplocks whose
1330 * TargetOplockKey is equal to Open.TargetOplockKey
1331 * (there is supposed to be at most one present):
1332 * XXX: Note, the spec. was missing a step:
1333 * XXX: Remove the open from RHOplocks
1334 * XXX: Confirm with MS dochelp
1335 * * Notify the server of an oplock break
1336 * according to the algorithm in section
1337 * 2.1.5.17.3, setting the algorithm's
1338 * parameters as follows:
1339 * * BreakingOplockOpen = ThisOpen
1340 * * NewOplockLevel =
1341 * (READ_CACHING|HANDLE_CACHING)
1342 * * AcknowledgeRequired = FALSE
1343 * * OplockCompletionStatus =
1344 * STATUS_..._NEW_HANDLE
1345 * (The operation does not end at this point;
1346 * this call to 2.1.5.17.3 completes some
1347 * earlier call to 2.1.5.17.2.)
1348 * EndIf
1349 *
1350 * If this SMB2 lease already has an "RH" handle,
1351 * we'll update that lease locally to point to
1352 * this new handle.
1353 */
1354 FOREACH_NODE_OFILE(node, o) {
1355 if (o->f_oplock.onlist_RH == 0)
1356 continue;
1357 if (CompareOplockKeys(ofile, o, 0)) {
1358 o->f_oplock.onlist_RH = B_FALSE;
1359 node->n_oplock.cnt_RH--;
1360 ASSERT(node->n_oplock.cnt_RH >= 0);
1361 smb_oplock_ind_break(o,
1362 CACHE_RH, B_FALSE,
1363 STATUS_NEW_HANDLE);
1364 }
1365 }
1366 } /* EndIf !GrantingInAck */
1367
1368 /*
1369 * Add Open to Open.Stream.Oplock.RHOplocks.
1370 */
1371 if (ofile->f_oplock.onlist_RH == B_FALSE) {
1372 ofile->f_oplock.onlist_RH = B_TRUE;
1373 node->n_oplock.cnt_RH++;
1374 }
1375
1376 /*
1377 * Recompute Open.Stream.Oplock.State according to the
1378 * algorithm in section 2.1.4.13, passing Open.Stream.Oplock
1379 * as the ThisOplock parameter.
1380 * Set OplockGranted to TRUE.
1381 */
1382 RecomputeOplockState(node);
1383 OplockGranted = B_TRUE;
1384 break;
1385
1386 default:
1387 /* No other value of RequestedOplock is possible. */
1388 ASSERT(0);
1389 status = NT_STATUS_OPLOCK_NOT_GRANTED;
1390 goto out;
1391 } /* EndSwitch (RequestedOplock) */
1392
1393 /*
1394 * If OplockGranted is TRUE:
1395 * This operation MUST be made cancelable by inserting it into
1396 * CancelableOperations.CancelableOperationList.
1397 * The operation waits until the oplock is broken or canceled,
1398 * as specified in section 2.1.5.17.3.
1399 * When the operation specified in section 2.1.5.17.3 is called,
1400 * its following input parameters are transferred to this routine
1401 * and returned by it:
1402 * Status is set to OplockCompletionStatus from the
1403 * operation specified in section 2.1.5.17.3.
1404 * NewOplockLevel is set to NewOplockLevel from the
1405 * operation specified in section 2.1.5.17.3.
1406 * AcknowledgeRequired is set to AcknowledgeRequired from
1407 * the operation specified in section 2.1.5.17.3.
1408 * EndIf
1409 */
1410 if (OplockGranted) {
1411 /* Note: *rop already set. */
1412 if ((node->n_oplock.ol_state & BREAK_ANY) != 0) {
1413 status = NT_STATUS_OPLOCK_BREAK_IN_PROGRESS;
1414 /* Caller does smb_oplock_wait_break() */
1415 } else {
1416 status = NT_STATUS_SUCCESS;
1417 }
1418
1419 /*
1420 * First oplock grant installs FEM hooks.
1421 */
1422 if (node->n_oplock.ol_fem == B_FALSE) {
1423 if (smb_fem_oplock_install(node) != 0) {
1424 cmn_err(CE_NOTE,
1425 "smb_fem_oplock_install failed");
1426 } else {
1427 node->n_oplock.ol_fem = B_TRUE;
1428 }
1429 }
1430 }
1431
1432 out:
1433 if (status == NT_STATUS_OPLOCK_NOT_GRANTED)
1434 *rop = LEVEL_NONE;
1435
1436 return (status);
1437 }
1438
1439 /*
1440 * 2.1.5.17.3 Indicating an Oplock Break to the Server
1441 * See smb_srv_oplock.c
1442 */
1443
1444 /*
1445 * 2.1.5.18 Server Acknowledges an Oplock Break
1446 *
1447 * The server provides:
1448 * Open - The Open associated with the oplock that has broken.
1449 * Type - As part of the acknowledgement, the server indicates a
1450 * new oplock it would like in place of the one that has broken.
1451 * Valid values are as follows:
1452 * LEVEL_NONE
1453 * LEVEL_TWO
1454 * LEVEL_GRANULAR - If this oplock type is specified,
1455 * the server additionally provides:
1456 * RequestedOplockLevel - A combination of zero or more of
1457 * the following flags:
1458 * READ_CACHING
1459 * HANDLE_CACHING
1460 * WRITE_CACHING
1461 *
1462 * If the server requests a new oplock and it is granted, the request
1463 * does not complete until the oplock is broken; the operation waits for
1464 * this to happen. Processing of an oplock break is described in
1465 * section 2.1.5.17.3. Whether the new oplock is granted or not, the
1466 * object store MUST return:
1467 *
1468 * Status - An NTSTATUS code indicating the result of the operation.
1469 *
1470 * If the server requests a new oplock and it is granted, then when the
1471 * oplock breaks and the request finally completes, the object store MUST
1472 * additionally return:
1473 * NewOplockLevel: The type of oplock the requested oplock has
1474 * been broken to. Valid values are as follows:
1475 * LEVEL_NONE (that is, no oplock)
1476 * LEVEL_TWO
1477 * A combination of one or more of the following flags:
1478 * READ_CACHING
1479 * HANDLE_CACHING
1480 * WRITE_CACHING
1481 * AcknowledgeRequired: A Boolean value; TRUE if the server MUST
1482 * acknowledge the oplock break, FALSE if not, as specified in
1483 * section 2.1.5.17.2.
1484 *
1485 * Note: Stores NewOplockLevel in *rop
1486 */
1487 uint32_t
smb_oplock_ack_break(smb_request_t * sr,smb_ofile_t * ofile,uint32_t * rop)1488 smb_oplock_ack_break(
1489 smb_request_t *sr,
1490 smb_ofile_t *ofile,
1491 uint32_t *rop)
1492 {
1493 smb_node_t *node = ofile->f_node;
1494 uint32_t type = *rop & OPLOCK_LEVEL_TYPE_MASK;
1495 uint32_t level = *rop & OPLOCK_LEVEL_CACHE_MASK;
1496 uint32_t status = NT_STATUS_SUCCESS;
1497 uint32_t BreakToLevel;
1498 boolean_t NewOplockGranted = B_FALSE;
1499 boolean_t ReturnBreakToNone = B_FALSE;
1500 boolean_t FoundMatchingRHOplock = B_FALSE;
1501 int other_keys;
1502
1503 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
1504 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
1505
1506 /*
1507 * If Open.Stream.Oplock is empty, the operation MUST be
1508 * failed with Status set to STATUS_INVALID_OPLOCK_PROTOCOL.
1509 */
1510 if (node->n_oplock.ol_state == 0) {
1511 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
1512 goto out;
1513 }
1514
1515 if (type == LEVEL_NONE || type == LEVEL_TWO) {
1516 /*
1517 * If Open.Stream.Oplock.ExclusiveOpen is not equal to Open,
1518 * the operation MUST be failed with Status set to
1519 * STATUS_INVALID_OPLOCK_PROTOCOL.
1520 */
1521 if (node->n_oplock.excl_open != ofile) {
1522 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
1523 goto out;
1524 }
1525
1526 /*
1527 * If Type is LEVEL_TWO and Open.Stream.Oplock.State
1528 * contains BREAK_TO_TWO:
1529 * Set Open.Stream.Oplock.State to LEVEL_TWO_OPLOCK.
1530 * Set NewOplockGranted to TRUE.
1531 */
1532 if (type == LEVEL_TWO &&
1533 (node->n_oplock.ol_state & BREAK_TO_TWO) != 0) {
1534 node->n_oplock.ol_state = LEVEL_TWO;
1535 NewOplockGranted = B_TRUE;
1536 }
1537
1538 /*
1539 * Else If Open.Stream.Oplock.State contains
1540 * BREAK_TO_TWO or BREAK_TO_NONE:
1541 * Set Open.Stream.Oplock.State to NO_OPLOCK.
1542 */
1543 else if ((node->n_oplock.ol_state &
1544 (BREAK_TO_TWO | BREAK_TO_NONE)) != 0) {
1545 node->n_oplock.ol_state = NO_OPLOCK;
1546 }
1547
1548 /*
1549 * Else If Open.Stream.Oplock.State contains
1550 * BREAK_TO_TWO_TO_NONE:
1551 * Set Open.Stream.Oplock.State to NO_OPLOCK.
1552 * Set ReturnBreakToNone to TRUE.
1553 */
1554 else if ((node->n_oplock.ol_state &
1555 BREAK_TO_TWO_TO_NONE) != 0) {
1556 node->n_oplock.ol_state = NO_OPLOCK;
1557 ReturnBreakToNone = B_TRUE;
1558 }
1559
1560 /*
1561 * Else
1562 * The operation MUST be failed with Status set to
1563 * STATUS_INVALID_OPLOCK_PROTOCOL.
1564 */
1565 else {
1566 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
1567 goto out;
1568 }
1569
1570 /*
1571 * For each Open WaitingOpen on Open.Stream.Oplock.WaitList:
1572 * Indicate that the operation associated with
1573 * WaitingOpen can continue according to the
1574 * algorithm in section 2.1.4.12.1, setting
1575 * OpenToRelease = WaitingOpen.
1576 * Remove WaitingOpen from Open.Stream.Oplock.WaitList.
1577 * EndFor
1578 */
1579 if (node->n_oplock.waiters)
1580 cv_broadcast(&node->n_oplock.WaitingOpenCV);
1581
1582 /*
1583 * Set Open.Stream.Oplock.ExclusiveOpen to NULL.
1584 */
1585 node->n_oplock.excl_open = NULL;
1586
1587 if (NewOplockGranted) {
1588 /*
1589 * The operation waits until the newly-granted
1590 * Level 2 oplock is broken, as specified in
1591 * section 2.1.5.17.3.
1592 *
1593 * Here we have just Ack'ed a break-to-II
1594 * so now get the level II oplock. We also
1595 * checked for break-to-none above, so this
1596 * will not need to wait for oplock breaks.
1597 */
1598 status = smb_oplock_req_shared(ofile, rop, B_TRUE);
1599 }
1600
1601 else if (ReturnBreakToNone) {
1602 /*
1603 * In this case the server was expecting the oplock
1604 * to break to Level 2, but because the oplock is
1605 * actually breaking to None (that is, no oplock),
1606 * the object store MUST indicate an oplock break
1607 * to the server according to the algorithm in
1608 * section 2.1.5.17.3, setting the algorithm's
1609 * parameters as follows:
1610 * BreakingOplockOpen = Open.
1611 * NewOplockLevel = LEVEL_NONE.
1612 * AcknowledgeRequired = FALSE.
1613 * OplockCompletionStatus = STATUS_SUCCESS.
1614 * (Because BreakingOplockOpen is equal to the
1615 * passed-in Open, the operation ends at this point.)
1616 */
1617 smb_oplock_ind_break_in_ack(
1618 sr, ofile,
1619 LEVEL_NONE, B_FALSE);
1620 }
1621 status = NT_STATUS_SUCCESS;
1622 goto out;
1623 } /* LEVEL_NONE or LEVEL_TWO */
1624
1625 if (type != LEVEL_GRANULAR) {
1626 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
1627 goto out;
1628 }
1629
1630 /* LEVEL_GRANULAR */
1631
1632 /*
1633 * Let BREAK_LEVEL_MASK = (BREAK_TO_READ_CACHING |
1634 * BREAK_TO_WRITE_CACHING | BREAK_TO_HANDLE_CACHING |
1635 * BREAK_TO_NO_CACHING),
1636 * R_AND_RH_GRANTED = (READ_CACHING | HANDLE_CACHING |
1637 * MIXED_R_AND_RH),
1638 * RH_GRANTED = (READ_CACHING | HANDLE_CACHING)
1639 *
1640 * (See BREAK_LEVEL_MASK in smb_oplock.h)
1641 */
1642 #define RH_GRANTED (READ_CACHING|HANDLE_CACHING)
1643 #define R_AND_RH_GRANTED (RH_GRANTED|MIXED_R_AND_RH)
1644
1645 /*
1646 * If there are no BREAK_LEVEL_MASK flags set, this is invalid,
1647 * unless the state is R_AND_RH_GRANTED or RH_GRANTED, in which
1648 * case we'll need to see if the RHBreakQueue is empty.
1649 */
1650
1651 /*
1652 * If (Open.Stream.Oplock.State does not contain any flag in
1653 * BREAK_LEVEL_MASK and
1654 * (Open.Stream.Oplock.State != R_AND_RH_GRANTED) and
1655 * (Open.Stream.Oplock.State != RH_GRANTED)) or
1656 * (((Open.Stream.Oplock.State == R_AND_RH_GRANTED) or
1657 * (Open.Stream.Oplock.State == RH_GRANTED)) and
1658 * Open.Stream.Oplock.RHBreakQueue is empty):
1659 * The request MUST be failed with Status set to
1660 * STATUS_INVALID_OPLOCK_PROTOCOL.
1661 * EndIf
1662 */
1663 if ((node->n_oplock.ol_state & BREAK_LEVEL_MASK) == 0) {
1664 if ((node->n_oplock.ol_state != R_AND_RH_GRANTED) &&
1665 (node->n_oplock.ol_state != RH_GRANTED)) {
1666 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
1667 goto out;
1668 }
1669 /* State is R_AND_RH_GRANTED or RH_GRANTED */
1670 if (node->n_oplock.cnt_RHBQ == 0) {
1671 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
1672 goto out;
1673 }
1674 }
1675
1676 /*
1677 * Compute the "Break To" cache level from the
1678 * BREAK_TO_... flags
1679 */
1680 switch (node->n_oplock.ol_state & BREAK_LEVEL_MASK) {
1681 case (BREAK_TO_READ_CACHING | BREAK_TO_WRITE_CACHING |
1682 BREAK_TO_HANDLE_CACHING):
1683 BreakToLevel = CACHE_RWH;
1684 break;
1685 case (BREAK_TO_READ_CACHING | BREAK_TO_WRITE_CACHING):
1686 BreakToLevel = CACHE_RW;
1687 break;
1688 case (BREAK_TO_READ_CACHING | BREAK_TO_HANDLE_CACHING):
1689 BreakToLevel = CACHE_RH;
1690 break;
1691 case BREAK_TO_READ_CACHING:
1692 BreakToLevel = READ_CACHING;
1693 break;
1694 case BREAK_TO_NO_CACHING:
1695 BreakToLevel = LEVEL_NONE;
1696 break;
1697 default:
1698 ASSERT(0);
1699 /* FALLTHROUGH */
1700 case 0:
1701 /*
1702 * This can happen when we have multiple RH opens,
1703 * and one of them breaks (RH to R). Happens in
1704 * the smbtorture smb2.lease.v2rename test.
1705 */
1706 BreakToLevel = CACHE_R;
1707 break;
1708 }
1709
1710 /* Switch Open.Stream.Oplock.State */
1711 switch (node->n_oplock.ol_state) {
1712
1713 case (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH):
1714 case (READ_CACHING|HANDLE_CACHING):
1715 case (READ_CACHING|HANDLE_CACHING|BREAK_TO_READ_CACHING):
1716 case (READ_CACHING|HANDLE_CACHING|BREAK_TO_NO_CACHING):
1717 /*
1718 * XXX: Missing from [MS-FSA]
1719 *
1720 * If we previously sent a break to none and the
1721 * client Ack level is R instead of none, we
1722 * need to send another break. We can then
1723 * proceed as if we got level = none.
1724 */
1725 if (level == CACHE_R && BreakToLevel == LEVEL_NONE) {
1726 smb_oplock_ind_break_in_ack(
1727 sr, ofile,
1728 LEVEL_NONE, B_FALSE);
1729 level = LEVEL_NONE;
1730 }
1731
1732 /*
1733 * For each RHOpContext ThisContext in
1734 * Open.Stream.Oplock.RHBreakQueue:
1735 * If ThisContext.Open equals Open:
1736 * (see below)
1737 *
1738 * Implementation skips the list walk, because
1739 * we can get the ofile directly.
1740 */
1741 if (ofile->f_oplock.onlist_RHBQ) {
1742 smb_ofile_t *o;
1743
1744 /*
1745 * Set FoundMatchingRHOplock to TRUE.
1746 * If ThisContext.BreakingToRead is FALSE:
1747 * If RequestedOplockLevel is not 0 and
1748 * Open.Stream.Oplock.WaitList is not empty:
1749 * The object store MUST indicate an
1750 * oplock break to the server according to
1751 * the algorithm in section 2.1.5.17.3,
1752 * setting the algorithm's params as follows:
1753 * BreakingOplockOpen = Open.
1754 * NewOplockLevel = LEVEL_NONE.
1755 * AcknowledgeRequired = TRUE.
1756 * OplockCompletionStatus =
1757 * STATUS_CANNOT_GRANT_...
1758 * (Because BreakingOplockOpen is equal to the
1759 * passed Open, the operation ends at this point.)
1760 * EndIf
1761 */
1762 FoundMatchingRHOplock = B_TRUE;
1763 if (ofile->f_oplock.BreakingToRead == B_FALSE) {
1764 if (level != 0 && node->n_oplock.waiters) {
1765 /* The ofile stays on RHBQ */
1766 smb_oplock_ind_break_in_ack(
1767 sr, ofile,
1768 LEVEL_NONE, B_TRUE);
1769 status = NT_STATUS_SUCCESS;
1770 goto out;
1771 }
1772 }
1773
1774 /*
1775 * Else // ThisContext.BreakingToRead is TRUE.
1776 * If Open.Stream.Oplock.WaitList is not empty and
1777 * (RequestedOplockLevel is CACHE_RW or CACHE_RWH:
1778 * The object store MUST indicate an oplock
1779 * break to the server according to the
1780 * algorithm in section 2.1.5.17.3, setting
1781 * the algorithm's parameters as follows:
1782 * * BreakingOplockOpen = Open
1783 * * NewOplockLevel = READ_CACHING
1784 * * AcknowledgeRequired = TRUE
1785 * * OplockCompletionStatus =
1786 * STATUS_CANNOT_GRANT...
1787 * (Because BreakingOplockOpen is equal to the
1788 * passed-in Open, the operation ends at this
1789 * point.)
1790 * EndIf
1791 * EndIf
1792 */
1793 else { /* BreakingToRead is TRUE */
1794 if (node->n_oplock.waiters &&
1795 (level == CACHE_RW ||
1796 level == CACHE_RWH)) {
1797 /* The ofile stays on RHBQ */
1798 smb_oplock_ind_break_in_ack(
1799 sr, ofile,
1800 CACHE_R, B_TRUE);
1801 status = NT_STATUS_SUCCESS;
1802 goto out;
1803 }
1804 }
1805
1806 /*
1807 * Remove ThisContext from Open...RHBreakQueue.
1808 */
1809 ofile->f_oplock.onlist_RHBQ = B_FALSE;
1810 node->n_oplock.cnt_RHBQ--;
1811 ASSERT(node->n_oplock.cnt_RHBQ >= 0);
1812
1813 /*
1814 * The operation waiting for the Read-Handle
1815 * oplock to break can continue if there are
1816 * no more Read-Handle oplocks outstanding, or
1817 * if all the remaining Read-Handle oplocks
1818 * have the same oplock key as the waiting
1819 * operation.
1820 *
1821 * For each Open WaitingOpen on Open...WaitList:
1822 *
1823 * * If (Open...RHBreakQueue is empty) or
1824 * (all RHOpContext.Open.TargetOplockKey values
1825 * on Open.Stream.Oplock.RHBreakQueue are
1826 * equal to WaitingOpen.TargetOplockKey):
1827 * * Indicate that the operation assoc.
1828 * with WaitingOpen can continue
1829 * according to the algorithm in
1830 * section 2.1.4.12.1, setting
1831 * OpenToRelease = WaitingOpen.
1832 * * Remove WaitingOpen from
1833 * Open.Stream.Oplock.WaitList.
1834 * * EndIf
1835 * EndFor
1836 */
1837 other_keys = 0;
1838 FOREACH_NODE_OFILE(node, o) {
1839 if (o->f_oplock.onlist_RHBQ == 0)
1840 continue;
1841 if (!CompareOplockKeys(ofile, o, 0))
1842 other_keys++;
1843 }
1844 if (other_keys == 0)
1845 cv_broadcast(&node->n_oplock.WaitingOpenCV);
1846
1847 /*
1848 * If RequestedOplockLevel is 0 (that is, no flags):
1849 * * Recompute Open.Stream.Oplock.State
1850 * according to the algorithm in section
1851 * 2.1.4.13, passing Open.Stream.Oplock as
1852 * the ThisOplock parameter.
1853 * * The algorithm MUST return Status set to
1854 * STATUS_SUCCESS at this point.
1855 */
1856 if (level == 0) {
1857 RecomputeOplockState(node);
1858 status = NT_STATUS_SUCCESS;
1859 goto out;
1860 }
1861
1862 /*
1863 * Else If RequestedOplockLevel does not contain
1864 * WRITE_CACHING:
1865 * * The object store MUST request a shared oplock
1866 * according to the algorithm in section
1867 * 2.1.5.17.2, setting the algorithm's
1868 * parameters as follows:
1869 * * Open = current Open.
1870 * * RequestedOplock =
1871 * RequestedOplockLevel.
1872 * * GrantingInAck = TRUE.
1873 * * The operation MUST at this point return any
1874 * status code returned by the shared oplock
1875 * request algorithm.
1876 */
1877 else if ((level & WRITE_CACHING) == 0) {
1878 *rop = level;
1879 status = smb_oplock_req_shared(
1880 ofile, rop, B_TRUE);
1881 goto out;
1882 }
1883
1884 /*
1885 * Set Open.Stream.Oplock.ExclusiveOpen to
1886 * ThisContext.Open.
1887 * Set Open.Stream.Oplock.State to
1888 * (RequestedOplockLevel|EXCLUSIVE).
1889 * This operation MUST be made cancelable by
1890 * inserting it into CancelableOperations...
1891 * This operation waits until the oplock is
1892 * broken or canceled, as specified in
1893 * section 2.1.5.17.3.
1894 *
1895 * Implementation note:
1896 *
1897 * Once we assing ol_state below, there
1898 * will be no BREAK_TO_... flags set,
1899 * so no need to wait for oplock breaks.
1900 */
1901 node->n_oplock.excl_open = ofile;
1902 node->n_oplock.ol_state = level | EXCLUSIVE;
1903 status = NT_STATUS_SUCCESS;
1904 } /* onlist_RHBQ */
1905 if (FoundMatchingRHOplock == B_FALSE) {
1906 /* The operation MUST be failed with Status... */
1907 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
1908 goto out;
1909 }
1910 break; /* case (READ_CACHING|HANDLE_CACHING...) */
1911
1912 case (READ_CACHING|WRITE_CACHING|EXCLUSIVE|BREAK_TO_READ_CACHING):
1913 case (READ_CACHING|WRITE_CACHING|EXCLUSIVE|BREAK_TO_NO_CACHING):
1914 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
1915 BREAK_TO_READ_CACHING|BREAK_TO_WRITE_CACHING):
1916 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
1917 BREAK_TO_READ_CACHING|BREAK_TO_HANDLE_CACHING):
1918 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
1919 BREAK_TO_READ_CACHING):
1920 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
1921 BREAK_TO_NO_CACHING):
1922 /*
1923 * If Open.Stream.Oplock.ExclusiveOpen != Open:
1924 * * The operation MUST be failed with Status set to
1925 * STATUS_INVALID_OPLOCK_PROTOCOL.
1926 * EndIf
1927 */
1928 if (node->n_oplock.excl_open != ofile) {
1929 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
1930 goto out;
1931 }
1932
1933 /*
1934 * If Open.Stream.Oplock.WaitList is not empty and
1935 * Open.Stream.Oplock.State does not contain HANDLE_CACHING
1936 * and RequestedOplockLevel is CACHE_RWH:
1937 * The object store MUST indicate an oplock break to
1938 * the server according to the algorithm in section
1939 * 2.1.5.17.3, setting the algorithm's params as follows:
1940 * * BreakingOplockOpen = Open.
1941 * * NewOplockLevel = BreakToLevel (see above)
1942 * * AcknowledgeRequired = TRUE.
1943 * * OplockCompletionStatus =
1944 * STATUS_CANNOT_GRANT_REQUESTED_OPLOCK.
1945 * (Because BreakingOplockOpen is equal to the passed-in
1946 * Open, the operation ends at this point.)
1947 */
1948 if (node->n_oplock.waiters &&
1949 (node->n_oplock.ol_state & HANDLE_CACHING) == 0 &&
1950 level == CACHE_RWH) {
1951 smb_oplock_ind_break_in_ack(
1952 sr, ofile,
1953 BreakToLevel, B_TRUE);
1954 status = NT_STATUS_SUCCESS;
1955 goto out;
1956 }
1957
1958 /*
1959 * Else If Open.Stream.IsDeleted is TRUE and
1960 * RequestedOplockLevel contains HANDLE_CACHING:
1961 */
1962 else if (((node->flags & NODE_FLAGS_DELETING) != 0) &&
1963 (level & HANDLE_CACHING) != 0) {
1964
1965 /*
1966 * The object store MUST indicate an oplock break to
1967 * the server according to the algorithm in section
1968 * 2.1.5.17.3, setting the algorithm's params as
1969 * follows:
1970 * * BreakingOplockOpen = Open.
1971 * * NewOplockLevel = RequestedOplockLevel
1972 * without HANDLE_CACHING (for example if
1973 * RequestedOplockLevel is
1974 * (READ_CACHING|HANDLE_CACHING), then
1975 * NewOplockLevel would be just READ_CACHING).
1976 * * AcknowledgeRequired = TRUE.
1977 * * OplockCompletionStatus =
1978 * STATUS_CANNOT_GRANT_REQUESTED_OPLOCK.
1979 * (Because BreakingOplockOpen is equal to the
1980 * passed-in Open, the operation ends at this point.)
1981 */
1982 level &= ~HANDLE_CACHING;
1983 smb_oplock_ind_break_in_ack(
1984 sr, ofile,
1985 level, B_TRUE);
1986 status = NT_STATUS_SUCCESS;
1987 goto out;
1988 }
1989
1990 /*
1991 * For each Open WaitingOpen on Open.Stream.Oplock.WaitList:
1992 * * Indicate that the operation associated with
1993 * WaitingOpen can continue according to the algorithm
1994 * in section 2.1.4.12.1, setting OpenToRelease
1995 * = WaitingOpen.
1996 * * Remove WaitingOpen from Open.Stream.Oplock.WaitList.
1997 * EndFor
1998 */
1999 cv_broadcast(&node->n_oplock.WaitingOpenCV);
2000
2001 /*
2002 * If RequestedOplockLevel does not contain WRITE_CACHING:
2003 * * Set Open.Stream.Oplock.ExclusiveOpen to NULL.
2004 * EndIf
2005 */
2006 if ((level & WRITE_CACHING) == 0) {
2007 node->n_oplock.excl_open = NULL;
2008 }
2009
2010 /*
2011 * If RequestedOplockLevel is 0 (that is, no flags):
2012 * * Set Open.Stream.Oplock.State to NO_OPLOCK.
2013 * * The operation returns Status set to STATUS_SUCCESS
2014 * at this point.
2015 */
2016 if (level == 0) {
2017 node->n_oplock.ol_state = NO_OPLOCK;
2018 status = NT_STATUS_SUCCESS;
2019 goto out;
2020 }
2021
2022 /*
2023 * Deal with possibly still pending breaks.
2024 * Two cases: R to none, RH to R or none.
2025 *
2026 * XXX: These two missing from [MS-FSA]
2027 */
2028
2029 /*
2030 * Breaking R to none.
2031 *
2032 * We sent break exclusive (RWH or RW) to none and
2033 * the client Ack reduces to R instead of to none.
2034 * Need to send another break. This is like:
2035 * "If BreakCacheLevel contains READ_CACHING..."
2036 * from smb_oplock_break_cmn.
2037 */
2038 if (level == CACHE_R && BreakToLevel == LEVEL_NONE) {
2039 smb_oplock_ind_break_in_ack(
2040 sr, ofile,
2041 LEVEL_NONE, B_FALSE);
2042 node->n_oplock.ol_state = NO_OPLOCK;
2043 status = NT_STATUS_SUCCESS;
2044 goto out;
2045 }
2046
2047 /*
2048 * Breaking RH to R or RH to none.
2049 *
2050 * We sent break from (RWH or RW) to (R or none),
2051 * and the client Ack reduces to RH instead of none.
2052 * Need to send another break. This is like:
2053 * "If BreakCacheLevel equals HANDLE_CACHING..."
2054 * from smb_oplock_break_cmn.
2055 *
2056 * Note: Windows always does break to CACHE_R here,
2057 * letting another Ack and ind_break round trip
2058 * take us the rest of the way from R to none.
2059 */
2060 if (level == CACHE_RH &&
2061 (BreakToLevel == CACHE_R ||
2062 BreakToLevel == LEVEL_NONE)) {
2063 smb_oplock_ind_break_in_ack(
2064 sr, ofile,
2065 CACHE_R, B_TRUE);
2066
2067 ofile->f_oplock.BreakingToRead =
2068 (BreakToLevel & READ_CACHING) ? 1: 0;
2069
2070 ASSERT(!(ofile->f_oplock.onlist_RHBQ));
2071 ofile->f_oplock.onlist_RHBQ = B_TRUE;
2072 node->n_oplock.cnt_RHBQ++;
2073
2074 RecomputeOplockState(node);
2075 status = NT_STATUS_SUCCESS;
2076 goto out;
2077 }
2078
2079 /*
2080 * Else If RequestedOplockLevel does not contain WRITE_CACHING:
2081 * * The object store MUST request a shared oplock
2082 * according to the algorithm in section 2.1.5.17.2,
2083 * setting the algorithm's parameters as follows:
2084 * * Pass in the current Open.
2085 * * RequestedOplock = RequestedOplockLevel.
2086 * * GrantingInAck = TRUE.
2087 * * The operation MUST at this point return any status
2088 * returned by the shared oplock request algorithm.
2089 */
2090 if ((level & WRITE_CACHING) == 0) {
2091 *rop = level;
2092 status = smb_oplock_req_shared(ofile, rop, B_TRUE);
2093 goto out;
2094 }
2095
2096 /*
2097 * Note that because this oplock is being set up as part of
2098 * an acknowledgement of an exclusive oplock break,
2099 * Open.Stream.Oplock.ExclusiveOpen was set
2100 * at the time of the original oplock request;
2101 * it contains Open.
2102 * * Set Open.Stream.Oplock.State to
2103 * (RequestedOplockLevel|EXCLUSIVE).
2104 * * This operation MUST be made cancelable...
2105 * * This operation waits until the oplock is broken or
2106 * canceled, as specified in section 2.1.5.17.3.
2107 *
2108 * Implementation notes:
2109 *
2110 * This can only be a break from RWH to RW.
2111 * The assignment of ol_state below means there will be
2112 * no BREAK_TO_... bits set, and therefore no need for
2113 * "waits until the oplock is broken" as described in
2114 * the spec for this bit of code. Therefore, this will
2115 * return SUCCESS instead of OPLOCK_BREAK_IN_PROGRESS.
2116 */
2117 ASSERT(node->n_oplock.excl_open == ofile);
2118 node->n_oplock.ol_state = level | EXCLUSIVE;
2119 status = NT_STATUS_SUCCESS;
2120 break; /* case (READ_CACHING|WRITE_CACHING|...) */
2121
2122 default:
2123 /* The operation MUST be failed with Status */
2124 status = NT_STATUS_INVALID_OPLOCK_PROTOCOL;
2125 break;
2126
2127 } /* Switch (oplock.state) */
2128
2129 out:
2130 if (status == NT_STATUS_INVALID_OPLOCK_PROTOCOL)
2131 *rop = LEVEL_NONE;
2132
2133 if (status == NT_STATUS_SUCCESS &&
2134 type == LEVEL_GRANULAR &&
2135 *rop != LEVEL_NONE) {
2136 *rop |= LEVEL_GRANULAR;
2137 }
2138
2139 /*
2140 * If this node no longer has any oplock grants, let's
2141 * go ahead and remove the FEM hooks now. We could leave
2142 * that until close, but this lets access outside of SMB
2143 * be free of FEM oplock work after a "break to none".
2144 */
2145 if (node->n_oplock.ol_state == NO_OPLOCK &&
2146 node->n_oplock.ol_fem == B_TRUE) {
2147 smb_fem_oplock_uninstall(node);
2148 node->n_oplock.ol_fem = B_FALSE;
2149 }
2150
2151 /*
2152 * The spec. describes waiting for a break here,
2153 * but we let the caller do that (when needed) if
2154 * status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS
2155 *
2156 * After some research, smb_oplock_ack_break()
2157 * never returns that status. Paranoid check.
2158 */
2159 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
2160 ASSERT(!"Unexpected OPLOCK_BREAK_IN_PROGRESS");
2161 status = NT_STATUS_SUCCESS;
2162 }
2163
2164 return (status);
2165 }
2166
2167 /*
2168 * 2.1.4.12 Algorithm to Check for an Oplock Break
2169 *
2170 * The inputs for this algorithm are:
2171 *
2172 * Open: The Open being used in the request calling this algorithm.
2173 *
2174 * Oplock: The Oplock being checked.
2175 *
2176 * Operation: A code describing the operation being processed.
2177 *
2178 * OpParams: Parameters associated with the Operation code that are
2179 * passed in from the calling request. For example, if Operation is
2180 * OPEN, as specified in section 2.1.5.1, then OpParams will have the
2181 * members DesiredAccess and CreateDisposition. Each of these is a
2182 * parameter to the open request as specified in section 2.1.5.1.
2183 * This parameter could be empty, depending on the Operation code.
2184 *
2185 * Flags: An optional parameter. If unspecified it is considered to
2186 * contain 0. Valid nonzero values are:
2187 * PARENT_OBJECT
2188 *
2189 * The algorithm uses the following local variables:
2190 *
2191 * Boolean values (initialized to FALSE):
2192 * BreakToTwo, BreakToNone, NeedToWait
2193 *
2194 * BreakCacheLevel – MAY contain 0 or a combination of one or more of
2195 * READ_CACHING, WRITE_CACHING, or HANDLE_CACHING, as specified in
2196 * section 2.1.1.10. Initialized to 0.
2197 * Note that there are only four legal nonzero combinations of flags
2198 * for BreakCacheLevel:
2199 * (READ_CACHING|WRITE_CACHING|HANDLE_CACHING)
2200 * (READ_CACHING|WRITE_CACHING)
2201 * WRITE_CACHING
2202 * HANDLE_CACHING
2203 *
2204 * Algorithm: (all)
2205 * If Oplock is not empty and Oplock.State is not NO_OPLOCK:
2206 * If Flags contains PARENT_OBJECT:
2207 * If Operation is OPEN, CLOSE, FLUSH_DATA,
2208 * FS_CONTROL(set_encryption) or
2209 * SET_INFORMATION(Basic, Allocation, EoF,
2210 * Rename, Link, Shortname, VDL):
2211 * Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING).
2212 * EndIf
2213 * Else // Normal operation (not PARENT_OBJECT)
2214 * Switch (Operation):
2215 * Case OPEN, CLOSE, ...
2216 * EndSwitch
2217 * EndIf // not parent
2218 * // Common section for all above
2219 * If BreakToTwo is TRUE:
2220 * ...
2221 * Else If BreakToNone
2222 * ...
2223 * EndIf
2224 * ...
2225 * EndIf
2226 *
2227 * This implementation uses separate functions for each of:
2228 * if (flags & PARENT)... else
2229 * switch (Operation)...
2230 */
2231
2232
2233 /*
2234 * If Flags contains PARENT_OBJECT:
2235 * ...
2236 * Note that this function is unusual in that the node arg is
2237 * the PARENT directory node, and ofile is NOT on the ofile list
2238 * of that directory but one of the nodes under it.
2239 *
2240 * Note that until we implement directory leases, this is a no-op.
2241 */
2242 uint32_t
smb_oplock_break_PARENT(smb_node_t * node,smb_ofile_t * ofile)2243 smb_oplock_break_PARENT(smb_node_t *node, smb_ofile_t *ofile)
2244 {
2245 uint32_t BreakCacheLevel;
2246
2247 /*
2248 * If Operation is OPEN, CLOSE, FLUSH_DATA,
2249 * FS_CONTROL(set_encryption) or
2250 * SET_INFORMATION(Basic, Allocation, EoF,
2251 * Rename, Link, Shortname, VDL):
2252 * Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING).
2253 * EndIf
2254 */
2255 BreakCacheLevel = PARENT_OBJECT |
2256 (READ_CACHING|WRITE_CACHING);
2257
2258 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
2259 }
2260
2261 /*
2262 * Helper for the cases where section 2.1.5.1 says:
2263 *
2264 * If Open.Stream.Oplock is not empty and Open.Stream.Oplock.State
2265 * contains BATCH_OPLOCK, the object store MUST check for an oplock
2266 * break according to the algorithm in section 2.1.4.12,
2267 * with input values as follows:
2268 * Open equal to this operation's Open
2269 * Oplock equal to Open.Stream.Oplock
2270 * Operation equal to "OPEN"
2271 * OpParams containing two members:
2272 * (DesiredAccess, CreateDisposition)
2273 *
2274 * So basically, just call smb_oplock_break_OPEN(), but
2275 * only if there's a batch oplock.
2276 */
2277 uint32_t
smb_oplock_break_BATCH(smb_node_t * node,smb_ofile_t * ofile,uint32_t DesiredAccess,uint32_t CreateDisposition)2278 smb_oplock_break_BATCH(smb_node_t *node, smb_ofile_t *ofile,
2279 uint32_t DesiredAccess, uint32_t CreateDisposition)
2280 {
2281 if ((node->n_oplock.ol_state & BATCH_OPLOCK) == 0)
2282 return (0);
2283
2284 return (smb_oplock_break_OPEN(node, ofile,
2285 DesiredAccess, CreateDisposition));
2286 }
2287
2288 /*
2289 * Case OPEN, as specified in section 2.1.5.1:
2290 *
2291 * Note: smb_ofile_open constructs a partially complete smb_ofile_t
2292 * for this call, which can be considerd a "proposed open". This
2293 * open may or may not turn into a usable open depending on what
2294 * happens in the remainder of the ofile_open code path.
2295 */
2296 uint32_t
smb_oplock_break_OPEN(smb_node_t * node,smb_ofile_t * ofile,uint32_t DesiredAccess,uint32_t CreateDisposition)2297 smb_oplock_break_OPEN(smb_node_t *node, smb_ofile_t *ofile,
2298 uint32_t DesiredAccess, uint32_t CreateDisposition)
2299 {
2300 uint32_t BreakCacheLevel = 0;
2301 /* BreakToTwo, BreakToNone, NeedToWait */
2302
2303 /*
2304 * If OpParams.DesiredAccess contains no flags other than
2305 * FILE_READ_ATTRIBUTES, FILE_WRITE_ATTRIBUTES, or SYNCHRONIZE,
2306 * the algorithm returns at this point.
2307 * EndIf
2308 */
2309 if ((DesiredAccess & ~(FILE_READ_ATTRIBUTES |
2310 FILE_WRITE_ATTRIBUTES | SYNCHRONIZE | READ_CONTROL)) == 0)
2311 return (0);
2312
2313 /*
2314 * If OpParams.CreateDisposition is FILE_SUPERSEDE,
2315 * FILE_OVERWRITE, or FILE_OVERWRITE_IF:
2316 * Set BreakToNone to TRUE, set BreakCacheLevel to
2317 * (READ_CACHING|WRITE_CACHING).
2318 * Else
2319 * Set BreakToTwo to TRUE,
2320 * set BreakCacheLevel to WRITE_CACHING.
2321 * EndIf
2322 */
2323 if (CreateDisposition == FILE_SUPERSEDE ||
2324 CreateDisposition == FILE_OVERWRITE ||
2325 CreateDisposition == FILE_OVERWRITE_IF) {
2326 BreakCacheLevel = BREAK_TO_NONE |
2327 (READ_CACHING|WRITE_CACHING);
2328 } else {
2329 /*
2330 * CreateDispositons: OPEN, OPEN_IF
2331 */
2332 BreakCacheLevel = BREAK_TO_TWO |
2333 WRITE_CACHING;
2334 }
2335
2336 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
2337 }
2338
2339 /*
2340 * Case OPEN_BREAK_H, as specified in section 2.1.5.1:
2341 * Set BreakCacheLevel to HANDLE_CACHING.
2342 * EndCase
2343 */
2344 uint32_t
smb_oplock_break_HANDLE(smb_node_t * node,smb_ofile_t * ofile)2345 smb_oplock_break_HANDLE(smb_node_t *node, smb_ofile_t *ofile)
2346 {
2347 uint32_t BreakCacheLevel = HANDLE_CACHING;
2348
2349 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
2350 }
2351
2352 /*
2353 * Case CLOSE, as specified in section 2.1.5.4:
2354 *
2355 * The MS-FSA spec. describes sending oplock break indications
2356 * (smb_oplock_ind_break ... NT_STATUS_OPLOCK_HANDLE_CLOSED)
2357 * for several cases where the ofile we're closing has some
2358 * oplock grants. We modify these slightly and use them to
2359 * clear out the SMB-level oplock state. We could probably
2360 * just skip most of these, as the caller knows this handle is
2361 * closing and could just discard the SMB-level oplock state.
2362 * For now, keeping this close to what the spec says.
2363 */
2364 void
smb_oplock_break_CLOSE(smb_node_t * node,smb_ofile_t * ofile)2365 smb_oplock_break_CLOSE(smb_node_t *node, smb_ofile_t *ofile)
2366 {
2367 smb_ofile_t *o;
2368
2369 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
2370 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
2371
2372 #ifdef DEBUG
2373 FOREACH_NODE_OFILE(node, o) {
2374 DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
2375 }
2376 #endif
2377
2378 /*
2379 * If Oplock.IIOplocks is not empty:
2380 * For each Open ThisOpen in Oplock.IIOplocks:
2381 * If ThisOpen == Open:
2382 * Remove ThisOpen from Oplock.IIOplocks.
2383 * Notify the server of an oplock break according to
2384 * the algorithm in section 2.1.5.17.3, setting the
2385 * algorithm's parameters as follows:
2386 * BreakingOplockOpen = ThisOpen.
2387 * NewOplockLevel = LEVEL_NONE.
2388 * AcknowledgeRequired = FALSE.
2389 * OplockCompletionStatus = STATUS_SUCCESS.
2390 * (The operation does not end at this point;
2391 * this call to 2.1.5.17.3 completes some
2392 * earlier call to 2.1.5.17.2.)
2393 * EndIf
2394 * EndFor
2395 * Recompute Oplock.State according to the algorithm in
2396 * section 2.1.4.13, passing Oplock as the ThisOplock parameter.
2397 * EndIf
2398 */
2399 if (node->n_oplock.cnt_II > 0) {
2400 o = ofile; /* No need for list walk */
2401 if (o->f_oplock.onlist_II) {
2402 o->f_oplock.onlist_II = B_FALSE;
2403 node->n_oplock.cnt_II--;
2404 ASSERT(node->n_oplock.cnt_II >= 0);
2405 /*
2406 * The spec. says to do:
2407 * smb_oplock_ind_break(o,
2408 * LEVEL_NONE, B_FALSE,
2409 * NT_STATUS_SUCCESS);
2410 *
2411 * We'll use STATUS_OPLOCK_HANDLE_CLOSED
2412 * like all the other ind_break calls in
2413 * this function, so the SMB-level will
2414 * just clear out its oplock state.
2415 */
2416 smb_oplock_ind_break(o,
2417 LEVEL_NONE, B_FALSE,
2418 NT_STATUS_OPLOCK_HANDLE_CLOSED);
2419 }
2420 RecomputeOplockState(node);
2421 }
2422
2423 /*
2424 * If Oplock.ROplocks is not empty:
2425 * For each Open ThisOpen in Oplock.ROplocks:
2426 * If ThisOpen == Open:
2427 * Remove ThisOpen from Oplock.ROplocks.
2428 * Notify the server of an oplock break according to
2429 * the algorithm in section 2.1.5.17.3, setting the
2430 * algorithm's parameters as follows:
2431 * BreakingOplockOpen = ThisOpen.
2432 * NewOplockLevel = LEVEL_NONE.
2433 * AcknowledgeRequired = FALSE.
2434 * OplockCompletionStatus =
2435 * STATUS_OPLOCK_HANDLE_CLOSED.
2436 * (The operation does not end at this point;
2437 * this call to 2.1.5.17.3 completes some
2438 * earlier call to 2.1.5.17.2.)
2439 * EndIf
2440 * EndFor
2441 * Recompute Oplock.State according to the algorithm in
2442 * section 2.1.4.13, passing Oplock as the ThisOplock parameter.
2443 * EndIf
2444 */
2445 if (node->n_oplock.cnt_R > 0) {
2446 o = ofile; /* No need for list walk */
2447 if (o->f_oplock.onlist_R) {
2448 o->f_oplock.onlist_R = B_FALSE;
2449 node->n_oplock.cnt_R--;
2450 ASSERT(node->n_oplock.cnt_R >= 0);
2451
2452 smb_oplock_ind_break(o,
2453 LEVEL_NONE, B_FALSE,
2454 NT_STATUS_OPLOCK_HANDLE_CLOSED);
2455 }
2456 RecomputeOplockState(node);
2457 }
2458
2459 /*
2460 * If Oplock.RHOplocks is not empty:
2461 * For each Open ThisOpen in Oplock.RHOplocks:
2462 * If ThisOpen == Open:
2463 * Remove ThisOpen from Oplock.RHOplocks.
2464 * Notify the server of an oplock break according to
2465 * the algorithm in section 2.1.5.17.3, setting the
2466 * algorithm's parameters as follows:
2467 * BreakingOplockOpen = ThisOpen.
2468 * NewOplockLevel = LEVEL_NONE.
2469 * AcknowledgeRequired = FALSE.
2470 * OplockCompletionStatus =
2471 * STATUS_OPLOCK_HANDLE_CLOSED.
2472 * (The operation does not end at this point;
2473 * this call to 2.1.5.17.3 completes some
2474 * earlier call to 2.1.5.17.2.)
2475 * EndIf
2476 * EndFor
2477 * Recompute Oplock.State according to the algorithm in
2478 * section 2.1.4.13, passing Oplock as the ThisOplock parameter.
2479 * EndIf
2480 */
2481 if (node->n_oplock.cnt_RH > 0) {
2482 o = ofile; /* No need for list walk */
2483 if (o->f_oplock.onlist_RH) {
2484 o->f_oplock.onlist_RH = B_FALSE;
2485 node->n_oplock.cnt_RH--;
2486 ASSERT(node->n_oplock.cnt_RH >= 0);
2487
2488 smb_oplock_ind_break(o,
2489 LEVEL_NONE, B_FALSE,
2490 NT_STATUS_OPLOCK_HANDLE_CLOSED);
2491 }
2492 RecomputeOplockState(node);
2493 }
2494
2495 /*
2496 * If Oplock.RHBreakQueue is not empty:
2497 * For each RHOpContext ThisContext in Oplock.RHBreakQueue:
2498 * If ThisContext.Open == Open:
2499 * Remove ThisContext from Oplock.RHBreakQueue.
2500 * EndIf
2501 * EndFor
2502 * Recompute Oplock.State according to the algorithm in
2503 * section 2.1.4.13, passing Oplock as the ThisOplock parameter.
2504 * For each Open WaitingOpen on Oplock.WaitList:
2505 * If Oplock.RHBreakQueue is empty:
2506 * (or) If the value of every
2507 * RHOpContext.Open.TargetOplockKey
2508 * on Oplock.RHBreakQueue is equal to
2509 * WaitingOpen .TargetOplockKey:
2510 * Indicate that the op. assoc. with
2511 * WaitingOpen can continue according to
2512 * the algorithm in section 2.1.4.12.1,
2513 * setting OpenToRelease = WaitingOpen.
2514 * Remove WaitingOpen from Oplock.WaitList.
2515 * EndIf
2516 * EndFor
2517 * EndIf
2518 */
2519 if (node->n_oplock.cnt_RHBQ > 0) {
2520 o = ofile; /* No need for list walk */
2521 if (o->f_oplock.onlist_RHBQ) {
2522 o->f_oplock.onlist_RHBQ = B_FALSE;
2523 node->n_oplock.cnt_RHBQ--;
2524 ASSERT(node->n_oplock.cnt_RHBQ >= 0);
2525 }
2526 RecomputeOplockState(node);
2527 /*
2528 * We don't keep a WaitingOpen list, so just
2529 * wake them all and let them look at the
2530 * updated Oplock.RHBreakQueue
2531 */
2532 cv_broadcast(&node->n_oplock.WaitingOpenCV);
2533 }
2534
2535 /*
2536 * If Open equals Open.Oplock.ExclusiveOpen
2537 * If Oplock.State contains none of (BREAK_ANY):
2538 * Notify the server of an oplock break according to
2539 * the algorithm in section 2.1.5.17.3, setting the
2540 * algorithm's parameters as follows:
2541 * BreakingOplockOpen = Oplock.ExclusiveOpen.
2542 * NewOplockLevel = LEVEL_NONE.
2543 * AcknowledgeRequired = FALSE.
2544 * OplockCompletionStatus equal to:
2545 * STATUS_OPLOCK_HANDLE_CLOSED if
2546 * Oplock.State contains any of
2547 * READ_CACHING, WRITE_CACHING, or
2548 * HANDLE_CACHING.
2549 * STATUS_SUCCESS otherwise.
2550 * (The operation does not end at this point;
2551 * this call to 2.1.5.17.3 completes some
2552 * earlier call to 2.1.5.17.1.)
2553 * EndIf
2554 * Set Oplock.ExclusiveOpen to NULL.
2555 * Set Oplock.State to NO_OPLOCK.
2556 * For each Open WaitingOpen on Oplock.WaitList:
2557 * Indicate that the operation associated with WaitingOpen
2558 * can continue according to the algorithm in section
2559 * 2.1.4.12.1, setting OpenToRelease = WaitingOpen.
2560 * Remove WaitingOpen from Oplock.WaitList.
2561 * EndFor
2562 * EndIf
2563 *
2564 * Modify this slightly from what the spec. says and only
2565 * up-call the break with status STATUS_OPLOCK_HANDLE_CLOSED.
2566 * The STATUS_SUCCESS case would do nothing at the SMB level,
2567 * so we'll just skip that part.
2568 */
2569 if (ofile == node->n_oplock.excl_open) {
2570 uint32_t level = node->n_oplock.ol_state & CACHE_RWH;
2571 if (level != 0 &&
2572 (node->n_oplock.ol_state & BREAK_ANY) == 0) {
2573 smb_oplock_ind_break(ofile,
2574 LEVEL_NONE, B_FALSE,
2575 NT_STATUS_OPLOCK_HANDLE_CLOSED);
2576 }
2577 node->n_oplock.excl_open = NULL;
2578 node->n_oplock.ol_state = NO_OPLOCK;
2579 cv_broadcast(&node->n_oplock.WaitingOpenCV);
2580 }
2581
2582 /*
2583 * The CLOSE sub-case of 2.1.5.4 (separate function here)
2584 * happens to always leave BreakCacheLevel=0 (see 2.1.5.4)
2585 * so there's never a need to call smb_oplock_break_cmn()
2586 * in this function. If that changed and we were to have
2587 * BreakCacheLevel != 0 here, then we'd need to call:
2588 * smb_oplock_break_cmn(node, ofile, BreakCacheLevel);
2589 */
2590
2591 if ((node->n_oplock.ol_state & BREAK_ANY) == 0)
2592 cv_broadcast(&node->n_oplock.WaitingOpenCV);
2593
2594 /*
2595 * If no longer any oplock, remove FEM hooks.
2596 */
2597 if (node->n_oplock.ol_state == NO_OPLOCK &&
2598 node->n_oplock.ol_fem == B_TRUE) {
2599 smb_fem_oplock_uninstall(node);
2600 node->n_oplock.ol_fem = B_FALSE;
2601 }
2602 }
2603
2604 /*
2605 * Case READ, as specified in section 2.1.5.2:
2606 * Set BreakToTwo to TRUE
2607 * Set BreakCacheLevel to WRITE_CACHING.
2608 * EndCase
2609 */
2610 uint32_t
smb_oplock_break_READ(smb_node_t * node,smb_ofile_t * ofile)2611 smb_oplock_break_READ(smb_node_t *node, smb_ofile_t *ofile)
2612 {
2613 uint32_t BreakCacheLevel = BREAK_TO_TWO | WRITE_CACHING;
2614
2615 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
2616 }
2617
2618 /*
2619 * Case FLUSH_DATA, as specified in section 2.1.5.6:
2620 * Set BreakToTwo to TRUE
2621 * Set BreakCacheLevel to WRITE_CACHING.
2622 * EndCase
2623 * Callers just use smb_oplock_break_READ() -- same thing.
2624 */
2625
2626 /*
2627 * Case LOCK_CONTROL, as specified in section 2.1.5.7:
2628 * Note: Spec does fall-through to WRITE here.
2629 *
2630 * Case WRITE, as specified in section 2.1.5.3:
2631 * Set BreakToNone to TRUE
2632 * Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING).
2633 * EndCase
2634 */
2635 uint32_t
smb_oplock_break_WRITE(smb_node_t * node,smb_ofile_t * ofile)2636 smb_oplock_break_WRITE(smb_node_t *node, smb_ofile_t *ofile)
2637 {
2638 uint32_t BreakCacheLevel = BREAK_TO_NONE |
2639 (READ_CACHING|WRITE_CACHING);
2640
2641 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
2642 }
2643
2644 /*
2645 * Case SET_INFORMATION, as specified in section 2.1.5.14:
2646 * Switch (OpParams.FileInformationClass):
2647 * Case FileEndOfFileInformation:
2648 * Case FileAllocationInformation:
2649 * Set BreakToNone to TRUE
2650 * Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING).
2651 * EndCase
2652 * Case FileRenameInformation:
2653 * Case FileLinkInformation:
2654 * Case FileShortNameInformation:
2655 * Set BreakCacheLevel to HANDLE_CACHING.
2656 * If Oplock.State contains BATCH_OPLOCK,
2657 * set BreakToNone to TRUE.
2658 * EndCase
2659 * Case FileDispositionInformation:
2660 * If OpParams.DeleteFile is TRUE,
2661 * Set BreakCacheLevel to HANDLE_CACHING.
2662 * EndCase
2663 * EndSwitch
2664 */
2665 uint32_t
smb_oplock_break_SETINFO(smb_node_t * node,smb_ofile_t * ofile,uint32_t InfoClass)2666 smb_oplock_break_SETINFO(smb_node_t *node, smb_ofile_t *ofile,
2667 uint32_t InfoClass)
2668 {
2669 uint32_t BreakCacheLevel = 0;
2670
2671 switch (InfoClass) {
2672 case FileEndOfFileInformation:
2673 case FileAllocationInformation:
2674 BreakCacheLevel = BREAK_TO_NONE |
2675 (READ_CACHING|WRITE_CACHING);
2676 break;
2677
2678 case FileRenameInformation:
2679 case FileLinkInformation:
2680 case FileShortNameInformation:
2681 BreakCacheLevel = HANDLE_CACHING;
2682 if (node->n_oplock.ol_state & BATCH_OPLOCK) {
2683 BreakCacheLevel |= BREAK_TO_NONE;
2684 }
2685 break;
2686 case FileDispositionInformation:
2687 /* Only called if (OpParams.DeleteFile is TRUE) */
2688 BreakCacheLevel = HANDLE_CACHING;
2689 break;
2690
2691 }
2692
2693 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
2694 }
2695
2696 /*
2697 * This one is not from the spec. It appears that Windows will
2698 * open a handle for an SMB1 delete call (at least internally).
2699 * We don't open a handle for delete, but do want to break as if
2700 * we had done, so this breaks like a combination of:
2701 * break_BATCH(... DELETE, FILE_OPEN_IF)
2702 * break_HANDLE(...)
2703 */
2704 uint32_t
smb_oplock_break_DELETE(smb_node_t * node,smb_ofile_t * ofile)2705 smb_oplock_break_DELETE(smb_node_t *node, smb_ofile_t *ofile)
2706 {
2707 uint32_t BreakCacheLevel = HANDLE_CACHING;
2708
2709 if ((node->n_oplock.ol_state & BATCH_OPLOCK) != 0)
2710 BreakCacheLevel |= BREAK_TO_TWO;
2711
2712 return (smb_oplock_break_cmn(node, ofile, BreakCacheLevel));
2713 }
2714
2715 /*
2716 * Case FS_CONTROL, as specified in section 2.1.5.9:
2717 * If OpParams.ControlCode is FSCTL_SET_ZERO_DATA:
2718 * Set BreakToNone to TRUE.
2719 * Set BreakCacheLevel to (READ_CACHING|WRITE_CACHING).
2720 * EndIf
2721 * EndCase
2722 * Callers just use smb_oplock_break_WRITE() -- same thing.
2723 */
2724
2725 /*
2726 * Common section for all cases above
2727 * Note: When called via FEM: ofile == NULL
2728 */
2729 static uint32_t
smb_oplock_break_cmn(smb_node_t * node,smb_ofile_t * ofile,uint32_t BreakCacheLevel)2730 smb_oplock_break_cmn(smb_node_t *node,
2731 smb_ofile_t *ofile, uint32_t BreakCacheLevel)
2732 {
2733 smb_oplock_t *nol = &node->n_oplock;
2734 uint32_t CmpFlags, status;
2735 boolean_t BreakToTwo, BreakToNone, NeedToWait;
2736 smb_ofile_t *o = NULL;
2737
2738 CmpFlags = (BreakCacheLevel & PARENT_OBJECT);
2739 BreakToTwo = (BreakCacheLevel & BREAK_TO_TWO) != 0;
2740 BreakToNone = (BreakCacheLevel & BREAK_TO_NONE) != 0;
2741 BreakCacheLevel &= (READ_CACHING | WRITE_CACHING | HANDLE_CACHING);
2742 NeedToWait = B_FALSE;
2743 status = NT_STATUS_SUCCESS;
2744
2745 smb_llist_enter(&node->n_ofile_list, RW_READER);
2746 mutex_enter(&node->n_oplock.ol_mutex);
2747
2748 #ifdef DEBUG
2749 FOREACH_NODE_OFILE(node, o) {
2750 DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
2751 }
2752 #endif
2753
2754 if (node->n_oplock.ol_state == 0 ||
2755 node->n_oplock.ol_state == NO_OPLOCK)
2756 goto out;
2757
2758 if (BreakToTwo) {
2759 /*
2760 * If (Oplock.State != LEVEL_TWO_OPLOCK) and
2761 * ((Oplock.ExclusiveOpen is empty) or
2762 * (Oplock.ExclusiveOpen.TargetOplockKey !=
2763 * Open.TargetOplockKey)):
2764 */
2765 if ((nol->ol_state != LEVEL_TWO_OPLOCK) &&
2766 (((o = nol->excl_open) == NULL) ||
2767 !CompareOplockKeys(ofile, o, CmpFlags))) {
2768
2769 /*
2770 * If (Oplock.State contains EXCLUSIVE) and
2771 * (Oplock.State contains none of READ_CACHING,
2772 * WRITE_CACHING, or HANDLE_CACHING):
2773 */
2774 if ((nol->ol_state & EXCLUSIVE) != 0 &&
2775 (nol->ol_state & CACHE_RWH) == 0) {
2776 /*
2777 * If Oplock.State contains none of:
2778 * BREAK_TO_NONE,
2779 * BREAK_TO_TWO,
2780 * BREAK_TO_TWO_TO_NONE,
2781 * BREAK_TO_READ_CACHING,
2782 * BREAK_TO_WRITE_CACHING,
2783 * BREAK_TO_HANDLE_CACHING,
2784 * BREAK_TO_NO_CACHING:
2785 */
2786 if ((nol->ol_state & BREAK_ANY) == 0) {
2787
2788 /*
2789 * Oplock.State MUST contain either
2790 * LEVEL_ONE_OPLOCK or BATCH_OPLOCK.
2791 * Set BREAK_TO_TWO in Oplock.State.
2792 */
2793 ASSERT((nol->ol_state &
2794 (LEVEL_ONE | LEVEL_BATCH)) != 0);
2795 nol->ol_state |= BREAK_TO_TWO;
2796
2797 /*
2798 * Notify the server of an oplock break
2799 * according to the algorithm in section
2800 * 2.1.5.17.3, setting the algorithm's
2801 * parameters as follows:
2802 * BreakingOplockOpen =
2803 * Oplock.ExclusiveOpen.
2804 * NewOplockLevel = LEVEL_TWO.
2805 * AcknowledgeRequired = TRUE.
2806 * Compl_Status = STATUS_SUCCESS.
2807 * (The operation does not end at this
2808 * point; this call to 2.1.5.17.3
2809 * completes some earlier call to
2810 * 2.1.5.17.1.)
2811 */
2812 o = nol->excl_open;
2813 ASSERT(o != NULL);
2814 smb_oplock_ind_break(o,
2815 LEVEL_TWO, B_TRUE,
2816 NT_STATUS_SUCCESS);
2817 }
2818
2819 /*
2820 * The operation that called this algorithm
2821 * MUST be made cancelable by ...
2822 * The operation that called this algorithm
2823 * waits until the oplock break is
2824 * acknowledged, as specified in section
2825 * 2.1.5.18, or the operation is canceled.
2826 */
2827 status = NT_STATUS_OPLOCK_BREAK_IN_PROGRESS;
2828 /* Caller does smb_oplock_wait_break() */
2829 }
2830 }
2831 } else if (BreakToNone) {
2832 /*
2833 * If (Oplock.State == LEVEL_TWO_OPLOCK) or
2834 * (Oplock.ExclusiveOpen is empty) or
2835 * (Oplock.ExclusiveOpen.TargetOplockKey !=
2836 * Open.TargetOplockKey):
2837 */
2838 if (nol->ol_state == LEVEL_TWO_OPLOCK ||
2839 (((o = nol->excl_open) == NULL) ||
2840 !CompareOplockKeys(ofile, o, CmpFlags))) {
2841
2842 /*
2843 * If (Oplock.State != NO_OPLOCK) and
2844 * (Oplock.State contains neither
2845 * WRITE_CACHING nor HANDLE_CACHING):
2846 */
2847 if (nol->ol_state != NO_OPLOCK &&
2848 (nol->ol_state &
2849 (WRITE_CACHING | HANDLE_CACHING)) == 0) {
2850
2851 /*
2852 * If Oplock.State contains none of:
2853 * LEVEL_TWO_OPLOCK,
2854 * BREAK_TO_NONE,
2855 * BREAK_TO_TWO,
2856 * BREAK_TO_TWO_TO_NONE,
2857 * BREAK_TO_READ_CACHING,
2858 * BREAK_TO_WRITE_CACHING,
2859 * BREAK_TO_HANDLE_CACHING, or
2860 * BREAK_TO_NO_CACHING:
2861 */
2862 if ((nol->ol_state &
2863 (LEVEL_TWO_OPLOCK | BREAK_ANY)) == 0) {
2864
2865 /*
2866 * There could be a READ_CACHING-only
2867 * oplock here. Those are broken later.
2868 *
2869 * If Oplock.State contains READ_CACHING
2870 * go to the LeaveBreakToNone label.
2871 * Set BREAK_TO_NONE in Oplock.State.
2872 */
2873 if ((nol->ol_state & READ_CACHING) != 0)
2874 goto LeaveBreakToNone;
2875 nol->ol_state |= BREAK_TO_NONE;
2876
2877 /*
2878 * Notify the server of an oplock break
2879 * according to the algorithm in section
2880 * 2.1.5.17.3, setting the algorithm's
2881 * parameters as follows:
2882 * BreakingOplockOpen =
2883 * Oplock.ExclusiveOpen.
2884 * NewOplockLevel = LEVEL_NONE.
2885 * AcknowledgeRequired = TRUE.
2886 * Commpl_Status = STATUS_SUCCESS.
2887 * (The operation does not end at this
2888 * point; this call to 2.1.5.17.3
2889 * completes some earlier call to
2890 * 2.1.5.17.1.)
2891 */
2892 o = nol->excl_open;
2893 ASSERT(o != NULL);
2894 smb_oplock_ind_break(o,
2895 LEVEL_NONE, B_TRUE,
2896 NT_STATUS_SUCCESS);
2897 }
2898
2899 /*
2900 * Else If Oplock.State equals LEVEL_TWO_OPLOCK
2901 * or (LEVEL_TWO_OPLOCK|READ_CACHING):
2902 */
2903 else if (nol->ol_state == LEVEL_TWO ||
2904 nol->ol_state == (LEVEL_TWO|READ_CACHING)) {
2905
2906 /*
2907 * For each Open O in Oplock.IIOplocks:
2908 * Remove O from Oplock.IIOplocks.
2909 * Notify the server of an oplock
2910 * break according to the algorithm
2911 * in section 2.1.5.17.3, setting the
2912 * algorithm's parameters as follows:
2913 * BreakingOplockOpen = ThisOpen.
2914 * NewOplockLevel = LEVEL_NONE.
2915 * AcknowledgeRequired = FALSE.
2916 * Compl_Status = STATUS_SUCCESS.
2917 * (The operation does not end at
2918 * this point; this call to
2919 * 2.1.5.17.3 completes some
2920 * earlier call to 2.1.5.17.2.)
2921 * EndFor
2922 */
2923 FOREACH_NODE_OFILE(node, o) {
2924 if (o->f_oplock.onlist_II == 0)
2925 continue;
2926 o->f_oplock.onlist_II = B_FALSE;
2927 nol->cnt_II--;
2928 ASSERT(nol->cnt_II >= 0);
2929
2930 smb_oplock_ind_break(o,
2931 LEVEL_NONE, B_FALSE,
2932 NT_STATUS_SUCCESS);
2933 }
2934 /*
2935 * If Oplock.State equals
2936 * (LEVEL_TWO_OPLOCK|READ_CACHING):
2937 * Set Oplock.State = READ_CACHING.
2938 * Else
2939 * Set Oplock.State = NO_OPLOCK.
2940 * EndIf
2941 * Go to the LeaveBreakToNone label.
2942 */
2943 if (nol->ol_state ==
2944 (LEVEL_TWO_OPLOCK | READ_CACHING)) {
2945 nol->ol_state = READ_CACHING;
2946 } else {
2947 nol->ol_state = NO_OPLOCK;
2948 }
2949 goto LeaveBreakToNone;
2950 }
2951
2952 /*
2953 * Else If Oplock.State contains BREAK_TO_TWO:
2954 * Clear BREAK_TO_TWO from Oplock.State.
2955 * Set BREAK_TO_TWO_TO_NONE in Oplock.State
2956 * EndIf
2957 */
2958 else if (nol->ol_state & BREAK_TO_TWO) {
2959 nol->ol_state &= ~BREAK_TO_TWO;
2960 nol->ol_state |= BREAK_TO_TWO_TO_NONE;
2961 }
2962
2963 /*
2964 * If Oplock.ExclusiveOpen is not empty,
2965 * and Oplock.Excl_Open.TargetOplockKey
2966 * equals Open.TargetOplockKey,
2967 * go to the LeaveBreakToNone label.
2968 */
2969 if ((o = nol->excl_open) != NULL &&
2970 CompareOplockKeys(ofile, o, CmpFlags))
2971 goto LeaveBreakToNone;
2972
2973 /*
2974 * The operation that called this algorithm
2975 * MUST be made cancelable by ...
2976 * The operation that called this algorithm
2977 * waits until the opl. break is acknowledged,
2978 * as specified in section 2.1.5.18, or the
2979 * operation is canceled.
2980 */
2981 status = NT_STATUS_OPLOCK_BREAK_IN_PROGRESS;
2982 /* Caller does smb_oplock_wait_break() */
2983 }
2984 }
2985 }
2986
2987 LeaveBreakToNone:
2988
2989 /*
2990 * if (BreakCacheLevel != 0) and (pp 37)
2991 * If Oplock.State contains any flags that are in BreakCacheLevel:
2992 * (Body of that "If" was here to just above the out label.)
2993 */
2994 if ((nol->ol_state & BreakCacheLevel) == 0)
2995 goto out;
2996
2997 /*
2998 * If Oplock.ExclusiveOpen is not empty, call the
2999 * algorithm in section 2.1.4.12.2, passing
3000 * Open as the OperationOpen parameter,
3001 * Oplock.ExclusiveOpen as the OplockOpen parameter,
3002 * and Flags as the Flagsparameter.
3003 * If the algorithm returns TRUE:
3004 * The algorithm returns at this point.
3005 */
3006 if ((o = nol->excl_open) != NULL &&
3007 CompareOplockKeys(ofile, o, CmpFlags) == B_TRUE) {
3008 status = NT_STATUS_SUCCESS;
3009 goto out;
3010 }
3011
3012 /*
3013 * Switch (Oplock.State):
3014 */
3015 switch (nol->ol_state) {
3016
3017 case (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH):
3018 case READ_CACHING:
3019 case (LEVEL_TWO_OPLOCK|READ_CACHING):
3020 /*
3021 * If BreakCacheLevel contains READ_CACHING:
3022 */
3023 if ((BreakCacheLevel & READ_CACHING) != 0) {
3024 /*
3025 * For each Open ThisOpen in Oplock.ROplocks:
3026 * Call the algorithm in section 2.1.4.12.2, pass:
3027 * Open as the OperationOpen parameter,
3028 * ThisOpen as the OplockOpen parameter,
3029 * and Flags as the Flagsparameter.
3030 * If the algorithm returns FALSE:
3031 * Remove ThisOpen from Oplock.ROplocks.
3032 * Notify the server of an oplock break
3033 * according to the algorithm in
3034 * section 2.1.5.17.3, setting the
3035 * algorithm's parameters as follows:
3036 * BreakingOplockOpen = ThisOpen.
3037 * NewOplockLevel = LEVEL_NONE.
3038 * AcknowledgeRequired = FALSE.
3039 * Compl_Status = STATUS_SUCCESS.
3040 * (The operation does not end at this point;
3041 * this call to 2.1.5.17.3 completes some
3042 * earlier call to 2.1.5.17.2.)
3043 * EndIf
3044 * EndFor
3045 */
3046 FOREACH_NODE_OFILE(node, o) {
3047 if (o->f_oplock.onlist_R == 0)
3048 continue;
3049 if (!CompareOplockKeys(ofile, o, CmpFlags)) {
3050 o->f_oplock.onlist_R = B_FALSE;
3051 nol->cnt_R--;
3052 ASSERT(nol->cnt_R >= 0);
3053
3054 smb_oplock_ind_break(o,
3055 LEVEL_NONE, B_FALSE,
3056 NT_STATUS_SUCCESS);
3057 }
3058 }
3059 }
3060 /*
3061 * If Oplock.State equals
3062 * (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH):
3063 * // Do nothing; FALL THROUGH to next Case statement.
3064 * Else
3065 * Recompute Oplock.State according to the
3066 * algorithm in section 2.1.4.13, passing
3067 * Oplock as the ThisOplock parameter.
3068 * EndIf
3069 */
3070 if (nol->ol_state ==
3071 (READ_CACHING|HANDLE_CACHING|MIXED_R_AND_RH))
3072 goto case_cache_rh;
3073
3074 RecomputeOplockState(node);
3075 break;
3076 /* EndCase XXX Note: spec. swapped this with prev. Endif. */
3077
3078 case_cache_rh:
3079 case (READ_CACHING|HANDLE_CACHING):
3080
3081 /*
3082 * If BreakCacheLevel equals HANDLE_CACHING:
3083 */
3084 if (BreakCacheLevel == HANDLE_CACHING) {
3085
3086 /*
3087 * For each Open ThisOpen in Oplock.RHOplocks:
3088 * If ThisOpen.OplockKey != Open.OplockKey:
3089 */
3090 FOREACH_NODE_OFILE(node, o) {
3091 if (o->f_oplock.onlist_RH == 0)
3092 continue;
3093 if (!CompareOplockKeys(ofile, o, CmpFlags)) {
3094
3095 /*
3096 * Remove ThisOpen from
3097 * Oplock.RHOplocks.
3098 */
3099 o->f_oplock.onlist_RH = B_FALSE;
3100 nol->cnt_RH--;
3101 ASSERT(nol->cnt_RH >= 0);
3102
3103 /*
3104 * Notify the server of an oplock break
3105 * according to the algorithm in
3106 * section 2.1.5.17.3, setting the
3107 * algorithm's parameters as follows:
3108 * BreakingOplockOpen = ThisOpen.
3109 * NewOplockLevel = READ_CACHING.
3110 * AcknowledgeRequired = TRUE.
3111 * Compl_Status = STATUS_SUCCESS.
3112 * (The operation does not end at this
3113 * point; this call to 2.1.5.17.3
3114 * completes some earlier call to
3115 * 2.1.5.17.2.)
3116 */
3117 smb_oplock_ind_break(o,
3118 READ_CACHING, B_TRUE,
3119 NT_STATUS_SUCCESS);
3120
3121 /*
3122 * Initialize a new RHOpContext object,
3123 * setting its fields as follows:
3124 * RHOpCtx.Open = ThisOpen.
3125 * RHOpCtx.BreakingToRead = TRUE.
3126 * Add the new RHOpContext object to
3127 * Oplock.RHBreakQueue.
3128 * Set NeedToWait to TRUE.
3129 */
3130 o->f_oplock.BreakingToRead = B_TRUE;
3131 ASSERT(!(o->f_oplock.onlist_RHBQ));
3132 o->f_oplock.onlist_RHBQ = B_TRUE;
3133 nol->cnt_RHBQ++;
3134
3135 NeedToWait = B_TRUE;
3136 }
3137 }
3138 }
3139
3140 /*
3141 * Else If BreakCacheLevel contains both
3142 * READ_CACHING and WRITE_CACHING:
3143 */
3144 else if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) ==
3145 (READ_CACHING | WRITE_CACHING)) {
3146
3147 /*
3148 * For each RHOpContext ThisContext in
3149 * Oplock.RHBreakQueue:
3150 * Call the algorithm in section 2.1.4.12.2,
3151 * passing Open as the OperationOpen parameter,
3152 * ThisContext.Open as the OplockOpen parameter,
3153 * and Flags as the Flags parameter.
3154 * If the algorithm returns FALSE:
3155 * Set ThisContext.BreakingToRead to FALSE.
3156 * If BreakCacheLevel & HANDLE_CACHING:
3157 * Set NeedToWait to TRUE.
3158 * EndIf
3159 * EndIf
3160 * EndFor
3161 */
3162 FOREACH_NODE_OFILE(node, o) {
3163 if (o->f_oplock.onlist_RHBQ == 0)
3164 continue;
3165 if (!CompareOplockKeys(ofile, o, CmpFlags)) {
3166 o->f_oplock.BreakingToRead = B_FALSE;
3167 if (BreakCacheLevel & HANDLE_CACHING)
3168 NeedToWait = B_TRUE;
3169 }
3170 }
3171
3172 /*
3173 * For each Open ThisOpen in Oplock.RHOplocks:
3174 * Call the algorithm in section 2.1.4.12.2,
3175 * passing Open as the OperationOpen parameter,
3176 * ThisOpen as the OplockOpen parameter, and
3177 * Flags as the Flagsparameter.
3178 * If the algorithm returns FALSE:
3179 * Remove ThisOpen from Oplock.RHOplocks.
3180 * Notify the server of an oplock break
3181 * according to the algorithm in
3182 * section 2.1.5.17.3, setting the
3183 * algorithm's parameters as follows:
3184 * BreakingOplockOpen = ThisOpen.
3185 * NewOplockLevel = LEVEL_NONE.
3186 * AcknowledgeRequired = TRUE.
3187 * Compl_Status = STATUS_SUCCESS.
3188 * (The operation does not end at this
3189 * point; this call to 2.1.5.17.3
3190 * completes some earlier call to
3191 * 2.1.5.17.2.)
3192 * Initialize a new RHOpContext object,
3193 * setting its fields as follows:
3194 * RHOpCtx.Open = ThisOpen.
3195 * RHOpCtx.BreakingToRead = FALSE
3196 * Add the new RHOpContext object to
3197 * Oplock.RHBreakQueue.
3198 * If BreakCacheLevel contains
3199 * HANDLE_CACHING:
3200 * Set NeedToWait to TRUE.
3201 * EndIf
3202 * EndIf
3203 * EndFor
3204 */
3205 FOREACH_NODE_OFILE(node, o) {
3206 if (o->f_oplock.onlist_RH == 0)
3207 continue;
3208 if (!CompareOplockKeys(ofile, o, CmpFlags)) {
3209 o->f_oplock.onlist_RH = B_FALSE;
3210 nol->cnt_RH--;
3211 ASSERT(nol->cnt_RH >= 0);
3212
3213 smb_oplock_ind_break(o,
3214 LEVEL_NONE, B_TRUE,
3215 NT_STATUS_SUCCESS);
3216
3217 o->f_oplock.BreakingToRead = B_FALSE;
3218 ASSERT(!(o->f_oplock.onlist_RHBQ));
3219 o->f_oplock.onlist_RHBQ = B_TRUE;
3220 nol->cnt_RHBQ++;
3221
3222 if (BreakCacheLevel & HANDLE_CACHING)
3223 NeedToWait = B_TRUE;
3224 }
3225 }
3226 }
3227
3228 // If the oplock is explicitly losing HANDLE_CACHING, RHBreakQueue is
3229 // not empty, and the algorithm has not yet decided to wait, this operation
3230 // might have to wait if there is an oplock on RHBreakQueue with a
3231 // non-matching key. This is done because even if this operation didn't
3232 // cause a break of a currently-granted Read-Handle caching oplock, it
3233 // might have done so had a currently-breaking oplock still been granted.
3234
3235 /*
3236 * If (NeedToWait is FALSE) and
3237 * (Oplock.RHBreakQueue is empty) and (XXX: Not empty)
3238 * (BreakCacheLevel contains HANDLE_CACHING):
3239 * For each RHOpContext ThisContex in Oplock.RHBreakQueue:
3240 * If ThisContext.Open.OplockKey != Open.OplockKey:
3241 * Set NeedToWait to TRUE.
3242 * Break out of the For loop.
3243 * EndIf
3244 * EndFor
3245 * EndIf
3246 * Recompute Oplock.State according to the algorithm in
3247 * section 2.1.4.13, passing Oplock as ThisOplock.
3248 */
3249 if (NeedToWait == B_FALSE &&
3250 (BreakCacheLevel & HANDLE_CACHING) != 0) {
3251 FOREACH_NODE_OFILE(node, o) {
3252 if (o->f_oplock.onlist_RHBQ == 0)
3253 continue;
3254 if (!CompareOplockKeys(ofile, o, CmpFlags)) {
3255 NeedToWait = B_TRUE;
3256 break;
3257 }
3258 }
3259 }
3260 RecomputeOplockState(node);
3261 break;
3262
3263 case (READ_CACHING|HANDLE_CACHING|BREAK_TO_READ_CACHING):
3264 /*
3265 * If BreakCacheLevel contains READ_CACHING:
3266 */
3267 if ((BreakCacheLevel & READ_CACHING) != 0) {
3268 /*
3269 * For each RHOpContext ThisContext in
3270 * Oplock.RHBreakQueue:
3271 * Call the algorithm in section 2.1.4.12.2,
3272 * passing Open = OperationOpen parameter,
3273 * ThisContext.Open = OplockOpen parameter,
3274 * and Flags as the Flags parameter.
3275 * If the algorithm returns FALSE:
3276 * Set ThisCtx.BreakingToRead = FALSE.
3277 * EndIf
3278 * Recompute Oplock.State according to the
3279 * algorithm in section 2.1.4.13, passing
3280 * Oplock as the ThisOplock parameter.
3281 * EndFor
3282 */
3283 FOREACH_NODE_OFILE(node, o) {
3284 if (o->f_oplock.onlist_RHBQ == 0)
3285 continue;
3286 if (!CompareOplockKeys(ofile, o, CmpFlags)) {
3287 o->f_oplock.BreakingToRead = B_FALSE;
3288 }
3289 }
3290 RecomputeOplockState(node);
3291 }
3292 /* FALLTHROUGH */
3293
3294 case (READ_CACHING|HANDLE_CACHING|BREAK_TO_NO_CACHING):
3295 /*
3296 * If BreakCacheLevel contains HANDLE_CACHING:
3297 * For each RHOpContext ThisContext in Oplock.RHBreakQueue:
3298 * If ThisContext.Open.OplockKey != Open.OplockKey:
3299 * Set NeedToWait to TRUE.
3300 * Break out of the For loop.
3301 * EndIf
3302 * EndFor
3303 * EndIf
3304 */
3305 if ((BreakCacheLevel & HANDLE_CACHING) != 0) {
3306 FOREACH_NODE_OFILE(node, o) {
3307 if (o->f_oplock.onlist_RHBQ == 0)
3308 continue;
3309 if (!CompareOplockKeys(ofile, o, CmpFlags)) {
3310 NeedToWait = B_TRUE;
3311 break;
3312 }
3313 }
3314 }
3315 break;
3316
3317 case (READ_CACHING|WRITE_CACHING|EXCLUSIVE):
3318 /*
3319 * If BreakCacheLevel contains both
3320 * READ_CACHING and WRITE_CACHING:
3321 * Notify the server of an oplock break according to
3322 * the algorithm in section 2.1.5.17.3, setting the
3323 * algorithm's parameters as follows:
3324 * BreakingOplockOpen = Oplock.ExclusiveOpen.
3325 * NewOplockLevel = LEVEL_NONE.
3326 * AcknowledgeRequired = TRUE.
3327 * OplockCompletionStatus = STATUS_SUCCESS.
3328 * (The operation does not end at this point;
3329 * this call to 2.1.5.17.3 completes some
3330 * earlier call to 2.1.5.17.1.)
3331 * Set Oplock.State to (READ_CACHING|WRITE_CACHING| \
3332 * EXCLUSIVE|BREAK_TO_NO_CACHING).
3333 * Set NeedToWait to TRUE.
3334 */
3335 if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) ==
3336 (READ_CACHING | WRITE_CACHING)) {
3337 o = nol->excl_open;
3338 ASSERT(o != NULL);
3339 smb_oplock_ind_break(o,
3340 LEVEL_NONE, B_TRUE,
3341 NT_STATUS_SUCCESS);
3342
3343 nol->ol_state =
3344 (READ_CACHING|WRITE_CACHING|
3345 EXCLUSIVE|BREAK_TO_NO_CACHING);
3346 NeedToWait = B_TRUE;
3347 }
3348
3349 /*
3350 * Else If BreakCacheLevel contains WRITE_CACHING:
3351 * Notify the server of an oplock break according to
3352 * the algorithm in section 2.1.5.17.3, setting the
3353 * algorithm's parameters as follows:
3354 * BreakingOplockOpen = Oplock.ExclusiveOpen.
3355 * NewOplockLevel = READ_CACHING.
3356 * AcknowledgeRequired = TRUE.
3357 * OplockCompletionStatus = STATUS_SUCCESS.
3358 * (The operation does not end at this point;
3359 * this call to 2.1.5.17.3 completes some
3360 * earlier call to 2.1.5.17.1.)
3361 * Set Oplock.State to (READ_CACHING|WRITE_CACHING|
3362 * EXCLUSIVE|BREAK_TO_READ_CACHING).
3363 * Set NeedToWait to TRUE.
3364 * EndIf
3365 */
3366 else if ((BreakCacheLevel & WRITE_CACHING) != 0) {
3367 o = nol->excl_open;
3368 ASSERT(o != NULL);
3369 smb_oplock_ind_break(o,
3370 READ_CACHING, B_TRUE,
3371 NT_STATUS_SUCCESS);
3372
3373 nol->ol_state =
3374 (READ_CACHING|WRITE_CACHING|
3375 EXCLUSIVE|BREAK_TO_READ_CACHING);
3376 NeedToWait = B_TRUE;
3377 }
3378 break;
3379
3380 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE):
3381 /*
3382 * If BreakCacheLevel equals WRITE_CACHING:
3383 * Notify the server of an oplock break according to
3384 * the algorithm in section 2.1.5.17.3, setting the
3385 * algorithm's parameters as follows:
3386 * BreakingOplockOpen = Oplock.ExclusiveOpen.
3387 * NewOplockLevel = (READ_CACHING|HANDLE_CACHING).
3388 * AcknowledgeRequired = TRUE.
3389 * OplockCompletionStatus = STATUS_SUCCESS.
3390 * (The operation does not end at this point;
3391 * this call to 2.1.5.17.3 completes some
3392 * earlier call to 2.1.5.17.1.)
3393 * Set Oplock.State to (READ_CACHING|WRITE_CACHING|
3394 * HANDLE_CACHING|EXCLUSIVE|
3395 * BREAK_TO_READ_CACHING|
3396 * BREAK_TO_HANDLE_CACHING).
3397 * Set NeedToWait to TRUE.
3398 */
3399 if (BreakCacheLevel == WRITE_CACHING) {
3400 o = nol->excl_open;
3401 ASSERT(o != NULL);
3402 smb_oplock_ind_break(o,
3403 CACHE_RH, B_TRUE,
3404 NT_STATUS_SUCCESS);
3405
3406 nol->ol_state =
3407 (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|
3408 EXCLUSIVE|BREAK_TO_READ_CACHING|
3409 BREAK_TO_HANDLE_CACHING);
3410 NeedToWait = B_TRUE;
3411 }
3412
3413 /*
3414 * Else If BreakCacheLevel equals HANDLE_CACHING:
3415 * Notify the server of an oplock break according to
3416 * the algorithm in section 2.1.5.17.3, setting the
3417 * algorithm's parameters as follows:
3418 * BreakingOplockOpen = Oplock.ExclusiveOpen.
3419 * NewOplockLevel = (READ_CACHING|WRITE_CACHING).
3420 * AcknowledgeRequired = TRUE.
3421 * OplockCompletionStatus = STATUS_SUCCESS.
3422 * (The operation does not end at this point;
3423 * this call to 2.1.5.17.3 completes some
3424 * earlier call to 2.1.5.17.1.)
3425 * Set Oplock.State to (READ_CACHING|WRITE_CACHING|
3426 * HANDLE_CACHING|EXCLUSIVE|
3427 * BREAK_TO_READ_CACHING|
3428 * BREAK_TO_WRITE_CACHING).
3429 * Set NeedToWait to TRUE.
3430 */
3431 else if (BreakCacheLevel == HANDLE_CACHING) {
3432 o = nol->excl_open;
3433 ASSERT(o != NULL);
3434 smb_oplock_ind_break(o,
3435 CACHE_RW, B_TRUE,
3436 NT_STATUS_SUCCESS);
3437
3438 nol->ol_state =
3439 (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|
3440 EXCLUSIVE|BREAK_TO_READ_CACHING|
3441 BREAK_TO_WRITE_CACHING);
3442 NeedToWait = B_TRUE;
3443 }
3444
3445 /*
3446 * Else If BreakCacheLevel contains both
3447 * READ_CACHING and WRITE_CACHING:
3448 * Notify the server of an oplock break according to
3449 * the algorithm in section 2.1.5.17.3, setting the
3450 * algorithm's parameters as follows:
3451 * BreakingOplockOpen = Oplock.ExclusiveOpen.
3452 * NewOplockLevel = LEVEL_NONE.
3453 * AcknowledgeRequired = TRUE.
3454 * OplockCompletionStatus = STATUS_SUCCESS.
3455 * (The operation does not end at this point;
3456 * this call to 2.1.5.17.3 completes some
3457 * earlier call to 2.1.5.17.1.)
3458 * Set Oplock.State to (READ_CACHING|WRITE_CACHING|
3459 * HANDLE_CACHING|EXCLUSIVE|
3460 * BREAK_TO_NO_CACHING).
3461 * Set NeedToWait to TRUE.
3462 * EndIf
3463 */
3464 else if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) ==
3465 (READ_CACHING | WRITE_CACHING)) {
3466 o = nol->excl_open;
3467 ASSERT(o != NULL);
3468 smb_oplock_ind_break(o,
3469 LEVEL_NONE, B_TRUE,
3470 NT_STATUS_SUCCESS);
3471
3472 nol->ol_state =
3473 (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|
3474 EXCLUSIVE|BREAK_TO_NO_CACHING);
3475 NeedToWait = B_TRUE;
3476 }
3477 break;
3478
3479 case (READ_CACHING|WRITE_CACHING|EXCLUSIVE|BREAK_TO_READ_CACHING):
3480 /*
3481 * If BreakCacheLevel contains READ_CACHING:
3482 * Set Oplock.State to (READ_CACHING|WRITE_CACHING|
3483 * EXCLUSIVE|BREAK_TO_NO_CACHING).
3484 * EndIf
3485 * If BreakCacheLevel contains either
3486 * READ_CACHING or WRITE_CACHING:
3487 * Set NeedToWait to TRUE.
3488 * EndIf
3489 */
3490 if ((BreakCacheLevel & READ_CACHING) != 0) {
3491 nol->ol_state =
3492 (READ_CACHING|WRITE_CACHING|
3493 EXCLUSIVE|BREAK_TO_NO_CACHING);
3494 }
3495 if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) != 0) {
3496 NeedToWait = B_TRUE;
3497 }
3498 break;
3499
3500 case (READ_CACHING|WRITE_CACHING|EXCLUSIVE|BREAK_TO_NO_CACHING):
3501 /*
3502 * If BreakCacheLevel contains either
3503 * READ_CACHING or WRITE_CACHING:
3504 * Set NeedToWait to TRUE.
3505 * EndIf
3506 */
3507 if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) != 0) {
3508 NeedToWait = B_TRUE;
3509 }
3510 break;
3511
3512 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
3513 BREAK_TO_READ_CACHING|BREAK_TO_WRITE_CACHING):
3514 /*
3515 * If BreakCacheLevel == WRITE_CACHING:
3516 * Set Oplock.State to (READ_CACHING|WRITE_CACHING|
3517 * HANDLE_CACHING|EXCLUSIVE|BREAK_TO_READ_CACHING).
3518 * Else If BreakCacheLevel contains both
3519 * READ_CACHING and WRITE_CACHING:
3520 * Set Oplock.State to (READ_CACHING|WRITE_CACHING|
3521 * HANDLE_CACHING|EXCLUSIVE|BREAK_TO_NO_CACHING).
3522 * EndIf
3523 * Set NeedToWait to TRUE.
3524 */
3525 if (BreakCacheLevel == WRITE_CACHING) {
3526 nol->ol_state = (READ_CACHING|WRITE_CACHING|
3527 HANDLE_CACHING|EXCLUSIVE|BREAK_TO_READ_CACHING);
3528 }
3529 else if ((BreakCacheLevel & (READ_CACHING | WRITE_CACHING)) ==
3530 (READ_CACHING | WRITE_CACHING)) {
3531 nol->ol_state = (READ_CACHING|WRITE_CACHING|
3532 HANDLE_CACHING|EXCLUSIVE|BREAK_TO_NO_CACHING);
3533 }
3534 NeedToWait = B_TRUE;
3535 break;
3536
3537 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
3538 BREAK_TO_READ_CACHING|BREAK_TO_HANDLE_CACHING):
3539 /*
3540 * If BreakCacheLevel == HANDLE_CACHING:
3541 * Set Oplock.State to (READ_CACHING|WRITE_CACHING|
3542 * HANDLE_CACHING|EXCLUSIVE|
3543 * BREAK_TO_READ_CACHING).
3544 * Else If BreakCacheLevel contains READ_CACHING:
3545 * Set Oplock.State to (READ_CACHING|WRITE_CACHING|
3546 * HANDLE_CACHING|EXCLUSIVE|
3547 * BREAK_TO_NO_CACHING).
3548 * EndIf
3549 * Set NeedToWait to TRUE.
3550 */
3551 if (BreakCacheLevel == HANDLE_CACHING) {
3552 nol->ol_state =
3553 (READ_CACHING|WRITE_CACHING|
3554 HANDLE_CACHING|EXCLUSIVE|
3555 BREAK_TO_READ_CACHING);
3556 }
3557 else if ((BreakCacheLevel & READ_CACHING) != 0) {
3558 nol->ol_state =
3559 (READ_CACHING|WRITE_CACHING|
3560 HANDLE_CACHING|EXCLUSIVE|
3561 BREAK_TO_NO_CACHING);
3562 }
3563 NeedToWait = B_TRUE;
3564 break;
3565
3566 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
3567 BREAK_TO_READ_CACHING):
3568 /*
3569 * If BreakCacheLevel contains READ_CACHING,
3570 * Set Oplock.State to (READ_CACHING|WRITE_CACHING|
3571 * HANDLE_CACHING|EXCLUSIVE|
3572 * BREAK_TO_NO_CACHING).
3573 * EndIf
3574 * Set NeedToWait to TRUE.
3575 */
3576 if ((BreakCacheLevel & READ_CACHING) != 0) {
3577 nol->ol_state =
3578 (READ_CACHING|WRITE_CACHING|
3579 HANDLE_CACHING|EXCLUSIVE|
3580 BREAK_TO_NO_CACHING);
3581 }
3582 NeedToWait = B_TRUE;
3583 break;
3584
3585 case (READ_CACHING|WRITE_CACHING|HANDLE_CACHING|EXCLUSIVE|
3586 BREAK_TO_NO_CACHING):
3587 NeedToWait = B_TRUE;
3588 break;
3589
3590 } /* Switch */
3591
3592 if (NeedToWait) {
3593 /*
3594 * The operation that called this algorithm MUST be
3595 * made cancelable by inserting it into
3596 * CancelableOperations.CancelableOperationList.
3597 * The operation that called this algorithm waits until
3598 * the oplock break is acknowledged, as specified in
3599 * section 2.1.5.18, or the operation is canceled.
3600 */
3601 status = NT_STATUS_OPLOCK_BREAK_IN_PROGRESS;
3602 /* Caller does smb_oplock_wait_break() */
3603 }
3604
3605 out:
3606 mutex_exit(&node->n_oplock.ol_mutex);
3607 smb_llist_exit(&node->n_ofile_list);
3608
3609 return (status);
3610 }
3611
3612 /*
3613 * smb_oplock_move()
3614 *
3615 * Helper function for smb2_lease_ofile_close, where we're closing the
3616 * ofile that has the oplock for a given lease, and need to move that
3617 * oplock to another handle with the same lease.
3618 *
3619 * This is not described in [MS-FSA], so presumably Windows does this
3620 * by keeping oplock objects separate from the open files (no action
3621 * needed in the FSA layer). We keep the oplock state as part of the
3622 * ofile, so we need to relocate the oplock state in this case.
3623 *
3624 * Note that in here, we're moving state for both the FSA level and
3625 * the SMB level (which is unusual) but this is the easiest way to
3626 * make sure we move the state without any other effects.
3627 */
3628 void
smb_oplock_move(smb_node_t * node,smb_ofile_t * fr_ofile,smb_ofile_t * to_ofile)3629 smb_oplock_move(smb_node_t *node,
3630 smb_ofile_t *fr_ofile, smb_ofile_t *to_ofile)
3631 {
3632 /*
3633 * These are the two common states for an ofile with
3634 * a lease that's not the one holding the oplock.
3635 * Log if it's not either of these.
3636 */
3637 static const smb_oplock_grant_t og0 = { 0 };
3638 static const smb_oplock_grant_t og8 = {
3639 .og_state = OPLOCK_LEVEL_GRANULAR, 0 };
3640 smb_oplock_grant_t og_tmp;
3641
3642 ASSERT(fr_ofile->f_node == node);
3643 ASSERT(to_ofile->f_node == node);
3644 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
3645
3646 /*
3647 * The ofile to which we're moving the oplock
3648 * should NOT have any oplock state. However,
3649 * as long as we just swap state between the
3650 * two oplocks, we won't invalidate any of
3651 * the node's "onlist" counts etc.
3652 */
3653 if (bcmp(&to_ofile->f_oplock, &og0, sizeof (og0)) != 0 &&
3654 bcmp(&to_ofile->f_oplock, &og8, sizeof (og8)) != 0) {
3655 #ifdef DEBUG
3656 cmn_err(CE_NOTE, "smb_oplock_move: not empty?");
3657 #endif
3658 DTRACE_PROBE2(dst__not__empty,
3659 smb_node_t *, node, smb_ofile_t *, to_ofile);
3660 }
3661
3662 og_tmp = to_ofile->f_oplock;
3663 to_ofile->f_oplock = fr_ofile->f_oplock;
3664 fr_ofile->f_oplock = og_tmp;
3665
3666 if (node->n_oplock.excl_open == fr_ofile)
3667 node->n_oplock.excl_open = to_ofile;
3668
3669 }
3670