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