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 2021 Tintri by DDN, Inc. All rights reserved.
14 * Copyright 2021-2023 RackTop Systems, Inc.
15 */
16
17 /*
18 * Dispatch function for SMB2_OPLOCK_BREAK
19 */
20
21 #include <smbsrv/smb2_kproto.h>
22 #include <smbsrv/smb_oplock.h>
23
24 /* StructSize for the two "break" message formats. */
25 #define SSZ_OPLOCK 24
26 #define SSZ_LEASE_ACK 36
27 #define SSZ_LEASE_BRK 44
28
29 #define NODE_FLAGS_DELETING (NODE_FLAGS_DELETE_ON_CLOSE |\
30 NODE_FLAGS_DELETE_COMMITTED)
31
32 static const char lease_zero[UUID_LEN] = { 0 };
33
34 static kmem_cache_t *smb_lease_cache = NULL;
35
36 void
smb2_lease_init()37 smb2_lease_init()
38 {
39 if (smb_lease_cache != NULL)
40 return;
41
42 smb_lease_cache = kmem_cache_create("smb_lease_cache",
43 sizeof (smb_lease_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
44 }
45
46 void
smb2_lease_fini()47 smb2_lease_fini()
48 {
49 if (smb_lease_cache != NULL) {
50 kmem_cache_destroy(smb_lease_cache);
51 smb_lease_cache = NULL;
52 }
53 }
54
55 /*
56 * Take a ref on the lease if it's not being destroyed.
57 * Returns false when the lease is being destroyed.
58 * Otherwise, returns true.
59 */
60 static boolean_t
smb2_lease_hold(smb_lease_t * ls)61 smb2_lease_hold(smb_lease_t *ls)
62 {
63 boolean_t ret;
64
65 mutex_enter(&ls->ls_mutex);
66 ret = !ls->ls_destroying;
67 if (ret)
68 ls->ls_refcnt++;
69 mutex_exit(&ls->ls_mutex);
70
71 return (ret);
72 }
73
74 static void
lease_destroy(smb_lease_t * ls)75 lease_destroy(smb_lease_t *ls)
76 {
77 smb_node_release(ls->ls_node);
78 mutex_destroy(&ls->ls_mutex);
79 kmem_cache_free(smb_lease_cache, ls);
80 }
81
82 void
smb2_lease_rele(smb_lease_t * ls)83 smb2_lease_rele(smb_lease_t *ls)
84 {
85 smb_llist_t *bucket;
86 boolean_t destroy = B_FALSE;
87
88 mutex_enter(&ls->ls_mutex);
89 ls->ls_refcnt--;
90 if (ls->ls_refcnt != 0 || ls->ls_destroying) {
91 mutex_exit(&ls->ls_mutex);
92 return;
93 }
94 ls->ls_destroying = B_TRUE;
95 mutex_exit(&ls->ls_mutex);
96
97 /*
98 * Get the list lock, then re-check the refcnt
99 * and if it's still zero, unlink & destroy.
100 */
101 bucket = ls->ls_bucket;
102 smb_llist_enter(bucket, RW_WRITER);
103
104 mutex_enter(&ls->ls_mutex);
105 if (ls->ls_refcnt == 0) {
106 smb_llist_remove(bucket, ls);
107 destroy = B_TRUE;
108 } else {
109 ls->ls_destroying = B_FALSE;
110 }
111 mutex_exit(&ls->ls_mutex);
112
113 smb_llist_exit(bucket);
114
115 if (destroy) {
116 lease_destroy(ls);
117 }
118 }
119
120 /*
121 * Compute a hash from a uuid
122 * Based on mod_hash_bystr()
123 */
124 static uint_t
smb_hash_uuid(const uint8_t * uuid)125 smb_hash_uuid(const uint8_t *uuid)
126 {
127 char *k = (char *)uuid;
128 uint_t hash = 0;
129 uint_t g;
130 int i;
131
132 ASSERT(k);
133 for (i = 0; i < UUID_LEN; i++) {
134 hash = (hash << 4) + k[i];
135 if ((g = (hash & 0xf0000000)) != 0) {
136 hash ^= (g >> 24);
137 hash ^= g;
138 }
139 }
140 return (hash);
141 }
142
143 /*
144 * Add or update a lease table entry for a new ofile.
145 * (in the per-session lease table)
146 * See [MS-SMB2] 3.3.5.9.8
147 * Handling the SMB2_CREATE_REQUEST_LEASE Create Context
148 */
149 uint32_t
smb2_lease_create(smb_request_t * sr,uint8_t * clnt)150 smb2_lease_create(smb_request_t *sr, uint8_t *clnt)
151 {
152 smb_arg_open_t *op = &sr->arg.open;
153 uint8_t *key = op->lease_key;
154 smb_ofile_t *of = sr->fid_ofile;
155 smb_hash_t *ht = sr->sr_server->sv_lease_ht;
156 smb_llist_t *bucket;
157 smb_lease_t *lease;
158 smb_lease_t *newlease;
159 size_t hashkey;
160 uint32_t status = NT_STATUS_INVALID_PARAMETER;
161
162 if (bcmp(key, lease_zero, UUID_LEN) == 0)
163 return (status);
164
165 /*
166 * Find or create, and add a ref for the new ofile.
167 */
168 hashkey = smb_hash_uuid(key);
169 hashkey &= (ht->num_buckets - 1);
170 bucket = &ht->buckets[hashkey].b_list;
171
172 newlease = kmem_cache_alloc(smb_lease_cache, KM_SLEEP);
173 bzero(newlease, sizeof (smb_lease_t));
174 mutex_init(&newlease->ls_mutex, NULL, MUTEX_DEFAULT, NULL);
175 newlease->ls_bucket = bucket;
176 newlease->ls_node = of->f_node;
177 smb_node_ref(newlease->ls_node);
178 newlease->ls_refcnt = 1;
179 newlease->ls_epoch = op->lease_epoch;
180 newlease->ls_version = op->lease_version;
181 bcopy(key, newlease->ls_key, UUID_LEN);
182 bcopy(clnt, newlease->ls_clnt, UUID_LEN);
183
184 smb_llist_enter(bucket, RW_WRITER);
185 for (lease = smb_llist_head(bucket); lease != NULL;
186 lease = smb_llist_next(bucket, lease)) {
187 /*
188 * Looking for this lease ID, on a node
189 * that's not being deleted.
190 */
191 if (bcmp(lease->ls_key, key, UUID_LEN) == 0 &&
192 bcmp(lease->ls_clnt, clnt, UUID_LEN) == 0 &&
193 (lease->ls_node->flags & NODE_FLAGS_DELETING) == 0 &&
194 smb2_lease_hold(lease))
195 break;
196 }
197
198 if (lease == NULL) {
199 lease = newlease;
200 smb_llist_insert_head(bucket, lease);
201 newlease = NULL; /* don't free */
202 }
203 smb_llist_exit(bucket);
204
205 /*
206 * If we found an existing lease, make sure it refers to the same node.
207 */
208 if (lease->ls_node != of->f_node) {
209 /* Same lease ID, different node! */
210 #ifdef DEBUG
211 cmn_err(CE_NOTE, "new lease on node %p (%s) "
212 "conflicts with existing node %p (%s)",
213 (void *) of->f_node,
214 of->f_node->od_name,
215 (void *) lease->ls_node,
216 lease->ls_node->od_name);
217 #endif
218 DTRACE_PROBE2(dup_lease, smb_request_t *, sr,
219 smb_lease_t *, lease);
220 smb2_lease_rele(lease);
221 lease = NULL; /* error */
222 }
223
224 if (newlease != NULL) {
225 lease_destroy(newlease);
226 }
227
228 if (lease != NULL) {
229 of->f_lease = lease;
230 status = NT_STATUS_SUCCESS;
231 }
232
233 return (status);
234 }
235
236 /*
237 * Find the lease for a given: client_uuid, lease_key
238 * Returns the lease with a new ref.
239 */
240 static smb_lease_t *
lease_lookup(smb_request_t * sr,uint8_t * lease_key)241 lease_lookup(smb_request_t *sr, uint8_t *lease_key)
242 {
243 smb_server_t *sv = sr->sr_server;
244 uint8_t *clnt_uuid = sr->session->clnt_uuid;
245 smb_hash_t *ht = sv->sv_lease_ht;
246 smb_llist_t *bucket;
247 smb_lease_t *lease;
248 size_t hashkey;
249
250 hashkey = smb_hash_uuid(lease_key);
251 hashkey &= (ht->num_buckets - 1);
252 bucket = &ht->buckets[hashkey].b_list;
253
254 smb_llist_enter(bucket, RW_READER);
255 lease = smb_llist_head(bucket);
256 while (lease != NULL) {
257 if (bcmp(lease->ls_key, lease_key, UUID_LEN) == 0 &&
258 bcmp(lease->ls_clnt, clnt_uuid, UUID_LEN) == 0 &&
259 smb2_lease_hold(lease))
260 break;
261 lease = smb_llist_next(bucket, lease);
262 }
263 smb_llist_exit(bucket);
264
265 return (lease);
266 }
267
268 /*
269 * Find the oplock smb_ofile_t for the specified lease.
270 * If no such ofile, NT_STATUS_UNSUCCESSFUL.
271 * On success, ofile (held) in sr->fid_ofile.
272 */
273 static uint32_t
lease_find_oplock(smb_request_t * sr,smb_lease_t * lease)274 lease_find_oplock(smb_request_t *sr, smb_lease_t *lease)
275 {
276 smb_node_t *node = lease->ls_node;
277 smb_ofile_t *o;
278 uint32_t status = NT_STATUS_UNSUCCESSFUL;
279
280 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
281 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
282 ASSERT(sr->fid_ofile == NULL);
283
284 FOREACH_NODE_OFILE(node, o) {
285 if (o->f_lease != lease)
286 continue;
287 if (o != lease->ls_oplock_ofile)
288 continue;
289 /*
290 * Found the ofile holding the oplock
291 * This hold released in smb_request_free
292 */
293 if (smb_ofile_hold_olbrk(o)) {
294 sr->fid_ofile = o;
295 status = NT_STATUS_SUCCESS;
296 break;
297 }
298 }
299
300 return (status);
301 }
302
303 /*
304 * This is called by smb2_oplock_break_ack when the struct size
305 * indicates this is a lease break (SZ_LEASE). See:
306 * [MS-SMB2] 3.3.5.22.2 Processing a Lease Acknowledgment
307 * This is an "Ack" from the client.
308 */
309 smb_sdrc_t
smb2_lease_break_ack(smb_request_t * sr)310 smb2_lease_break_ack(smb_request_t *sr)
311 {
312 smb_arg_olbrk_t *olbrk = &sr->arg.olbrk;
313 smb_lease_t *lease;
314 smb_node_t *node;
315 smb_ofile_t *ofile;
316 uint32_t LeaseState;
317 uint32_t status;
318 int rc = 0;
319
320 if (sr->session->dialect < SMB_VERS_2_1)
321 return (SDRC_ERROR);
322
323 /*
324 * Decode an SMB2 Lease Acknowldgement
325 * [MS-SMB2] 2.2.24.2
326 * Note: Struct size decoded by caller.
327 */
328 rc = smb_mbc_decodef(
329 &sr->smb_data, "6.#cl8.",
330 /* reserved 6. */
331 UUID_LEN, /* # */
332 olbrk->LeaseKey, /* c */
333 &olbrk->NewLevel); /* l */
334 /* duration 8. */
335 if (rc != 0)
336 return (SDRC_ERROR);
337 LeaseState = olbrk->NewLevel;
338
339 /*
340 * Find the lease via the given key.
341 */
342 lease = lease_lookup(sr, olbrk->LeaseKey);
343 if (lease == NULL) {
344 /*
345 * It's unusual to skip the dtrace start/done
346 * probes like this, but trying to run them
347 * with no lease->node would be complex and
348 * would not show anything particularly useful.
349 * Do the start probe after we find the ofile.
350 */
351 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
352 smb2sr_put_error(sr, status);
353 return (SDRC_SUCCESS);
354 }
355 // Note: lease ref; smb_lease_rele() below.
356 node = lease->ls_node;
357
358 /*
359 * Find the leased oplock. Hold locks so it can't move
360 * until we're done with ACK-break processing.
361 */
362 smb_llist_enter(&node->n_ofile_list, RW_READER);
363 mutex_enter(&node->n_oplock.ol_mutex);
364
365 status = lease_find_oplock(sr, lease);
366 /* Normally have sr->fid_ofile now. */
367
368 DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr);
369
370 if (status != 0) {
371 /* Leased oplock not found. Must have closed. */
372 goto errout;
373 }
374
375 /* Success, so have sr->fid_ofile */
376 ofile = sr->fid_ofile;
377
378 if (lease->ls_breaking == B_FALSE) {
379 /*
380 * This ACK is either unsolicited or too late,
381 * eg. we timed out the ACK and did it locally.
382 */
383 status = NT_STATUS_UNSUCCESSFUL;
384 goto errout;
385 }
386
387 /*
388 * If the new LeaseState has any bits in excess of
389 * the lease state we sent in the break, error...
390 */
391 if ((LeaseState & ~(lease->ls_breakto)) != 0) {
392 status = NT_STATUS_REQUEST_NOT_ACCEPTED;
393 goto errout;
394 }
395
396 /*
397 * Process the lease break ack.
398 *
399 * Clear breaking flags before we ack,
400 * because ack might set those.
401 * Signal both CVs, out of paranoia.
402 */
403 ofile->f_oplock.og_breaking = B_FALSE;
404 cv_broadcast(&ofile->f_oplock.og_ack_cv);
405 lease->ls_breaking = B_FALSE;
406 cv_broadcast(&lease->ls_ack_cv);
407
408 LeaseState |= OPLOCK_LEVEL_GRANULAR;
409 status = smb_oplock_ack_break(sr, ofile, &LeaseState);
410
411 ofile->f_oplock.og_state = LeaseState;
412 lease->ls_state = LeaseState;
413 /* ls_epoch does not change here */
414
415 if (ofile->dh_persist)
416 smb2_dh_update_oplock(sr, ofile);
417
418 errout:
419 sr->smb2_status = status;
420 DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr);
421
422 mutex_exit(&node->n_oplock.ol_mutex);
423 smb_llist_exit(&node->n_ofile_list);
424
425 smb2_lease_rele(lease);
426
427 if (status) {
428 smb2sr_put_error(sr, status);
429 return (SDRC_SUCCESS);
430 }
431
432 /*
433 * Encode an SMB2 Lease Ack. response
434 * [MS-SMB2] 2.2.25.2
435 */
436 LeaseState &= OPLOCK_LEVEL_CACHE_MASK;
437 (void) smb_mbc_encodef(
438 &sr->reply, "w6.#cl8.",
439 SSZ_LEASE_ACK, /* w */
440 /* reserved 6. */
441 UUID_LEN, /* # */
442 olbrk->LeaseKey, /* c */
443 LeaseState); /* l */
444 /* duration 8. */
445
446 return (SDRC_SUCCESS);
447
448 }
449
450 /*
451 * Compose an SMB2 Lease Break Notification packet, including
452 * the SMB2 header and everything, in sr->reply.
453 * The caller will send it and free the request.
454 *
455 * [MS-SMB2] 2.2.23.2 Lease Break Notification
456 */
457 static void
smb2_lease_break_notification(smb_request_t * sr,uint32_t OldLevel,uint32_t NewLevel,uint16_t Epoch,boolean_t AckReq)458 smb2_lease_break_notification(smb_request_t *sr,
459 uint32_t OldLevel, uint32_t NewLevel,
460 uint16_t Epoch, boolean_t AckReq)
461 {
462 smb_lease_t *ls = sr->fid_ofile->f_lease;
463 uint16_t Flags = 0;
464
465 /*
466 * Convert internal lease info to SMB2
467 */
468 if (AckReq)
469 Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
470 if (ls->ls_version < 2)
471 Epoch = 0;
472 OldLevel &= OPLOCK_LEVEL_CACHE_MASK;
473 NewLevel &= OPLOCK_LEVEL_CACHE_MASK;
474
475 /*
476 * SMB2 Header
477 */
478 sr->smb2_cmd_code = SMB2_OPLOCK_BREAK;
479 sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR;
480 sr->smb_tid = 0;
481 sr->smb_pid = 0;
482 sr->smb2_ssnid = 0;
483 sr->smb2_messageid = UINT64_MAX;
484 (void) smb2_encode_header(sr, B_FALSE);
485
486 /*
487 * SMB2 Oplock Break, variable part
488 *
489 * [MS-SMB2] says the current lease state preceeds the
490 * new lease state, but that looks like an error...
491 */
492 (void) smb_mbc_encodef(
493 &sr->reply, "wwl#cll4.4.4.",
494 SSZ_LEASE_BRK, /* w */
495 Epoch, /* w */
496 Flags, /* l */
497 SMB_LEASE_KEY_SZ, /* # */
498 ls->ls_key, /* c */
499 OldLevel, /* cur.st l */
500 NewLevel); /* new.st l */
501 /* reserved (4.4.4.) */
502 }
503
504 /*
505 * Do our best to send a lease break message to the client.
506 * When we get to multi-channel, this is supposed to try
507 * every channel before giving up. For now, try every
508 * connected session with an ofile sharing this lease.
509 *
510 * If this ofile has a valid session, try that first.
511 * Otherwise look on the node list for other ofiles with
512 * the same lease and a connected session.
513 */
514 static int
lease_send_any_cn(smb_request_t * sr)515 lease_send_any_cn(smb_request_t *sr)
516 {
517 smb_ofile_t *o;
518 smb_ofile_t *ofile = sr->fid_ofile;
519 smb_lease_t *lease = ofile->f_lease;
520 smb_node_t *node = ofile->f_node;
521 int rc = ENOTCONN;
522
523 /*
524 * If the passed oplock ofile has a session,
525 * this IF expression will be true.
526 */
527 if (sr->session == ofile->f_session) {
528 rc = smb_session_send(sr->session, 0, &sr->reply);
529 if (rc == 0)
530 return (rc);
531 }
532
533 smb_llist_enter(&node->n_ofile_list, RW_READER);
534 FOREACH_NODE_OFILE(node, o) {
535 if (o->f_lease != lease)
536 continue;
537 if (smb_ofile_hold(o)) {
538 /* Has a session. */
539 rc = smb_session_send(o->f_session, 0, &sr->reply);
540 smb_llist_post(&node->n_ofile_list, o,
541 smb_ofile_release_LL);
542 }
543 if (rc == 0)
544 break;
545 }
546 smb_llist_exit(&node->n_ofile_list);
547
548 return (rc);
549 }
550
551 /*
552 * See smb_llist_post on node->n_ofile_list below.
553 * Can't call smb_ofile_close with that list entered.
554 */
555 static void
lease_ofile_close_rele(void * arg)556 lease_ofile_close_rele(void *arg)
557 {
558 smb_ofile_t *of = (smb_ofile_t *)arg;
559
560 smb_ofile_close(of, 0);
561 smb_ofile_release(of);
562 }
563
564 /*
565 * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break
566 * If no connection, for each Open in Lease.LeaseOpens,
567 * the server MUST close the Open as specified in sec...
568 * for the following cases:
569 * - Open.IsDurable, Open.IsResilient, and
570 * Open.IsPersistent are all FALSE.
571 * - Open.IsDurable is TRUE and Lease.BreakToLeaseState
572 * does not contain SMB2_LEASE_HANDLE_CACHING and
573 */
574 static void
lease_close_notconn(smb_request_t * sr,uint32_t NewLevel)575 lease_close_notconn(smb_request_t *sr, uint32_t NewLevel)
576 {
577 smb_ofile_t *o;
578 smb_ofile_t *ofile = sr->fid_ofile;
579 smb_lease_t *lease = ofile->f_lease;
580 smb_node_t *node = ofile->f_node;
581
582 smb_llist_enter(&node->n_ofile_list, RW_READER);
583 FOREACH_NODE_OFILE(node, o) {
584 if (o->f_lease != lease)
585 continue;
586 if (o->f_oplock_closing)
587 continue;
588 if (o->dh_persist)
589 continue;
590 if (o->dh_vers == SMB2_RESILIENT)
591 continue;
592 if (o->dh_vers == SMB2_NOT_DURABLE ||
593 (NewLevel & OPLOCK_LEVEL_CACHE_HANDLE) == 0) {
594 if (smb_ofile_hold_olbrk(o)) {
595 smb_llist_post(&node->n_ofile_list, o,
596 lease_ofile_close_rele);
597 }
598 }
599 }
600 smb_llist_exit(&node->n_ofile_list);
601 }
602
603 /*
604 * Send a lease break over the wire, or if we can't,
605 * then process the lease break locally.
606 *
607 * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break
608 *
609 * This is mostly similar to smb2_oplock_send_break()
610 * See top comment there about the design.
611 *
612 * Differences beween a lease break and oplock break:
613 *
614 * Leases are an SMB-level mechanism whereby multiple open
615 * SMB file handles can share an oplock. All SMB handles
616 * on the lease enjoy the same caching rights. Down at the
617 * file-system level, just one oplock holds the cache rights
618 * for a lease, but (this is the tricky part) that oplock can
619 * MOVE among the SMB file handles sharing the lease. Such
620 * oplock moves can happen when a handle is closed (if that
621 * handle is the one with the oplock) or when a new open on
622 * the lease causes an upgrade of the caching rights.
623 *
624 * We have to deal here with lease movement because this call
625 * happens asynchronously after the smb_oplock_ind_break call,
626 * meaning that the oplock for the lease may have moved by the
627 * time this runs. In addition, the ofile holding the oplock
628 * might not be the best one to use to send a lease break.
629 * If the oplock is held by a handle that's "orphaned" and
630 * there are other handles on the lease with active sessions,
631 * we want to send the lease break on an active session.
632 *
633 * Also note: NewLevel (as provided by smb_oplock_ind_break etc.)
634 * does NOT include the GRANULAR flag. This level is expected to
635 * keep track of how each oplock was acquired (by lease or not)
636 * and put the GRANULAR flag back in when appropriate.
637 */
638 void
smb2_lease_send_break(smb_request_t * sr)639 smb2_lease_send_break(smb_request_t *sr)
640 {
641 smb_ofile_t *old_ofile;
642 smb_ofile_t *ofile = sr->fid_ofile;
643 smb_node_t *node = ofile->f_node;
644 smb_lease_t *lease = ofile->f_lease;
645 smb_arg_olbrk_t *olbrk = &sr->arg.olbrk;
646 boolean_t AckReq = olbrk->AckRequired;
647 uint32_t OldLevel = olbrk->OldLevel;
648 uint32_t NewLevel = olbrk->NewLevel;
649 uint32_t status;
650 int rc;
651
652 NewLevel |= OPLOCK_LEVEL_GRANULAR;
653
654 /*
655 * Build the break message in sr->reply.
656 * It's free'd in smb_request_free().
657 * Always an SMB2 lease here.
658 */
659 sr->reply.max_bytes = MLEN;
660 smb2_lease_break_notification(sr,
661 OldLevel, NewLevel, lease->ls_epoch, AckReq);
662
663 /*
664 * Try to send the break message to the client,
665 * on any connection with this lease.
666 */
667 rc = lease_send_any_cn(sr);
668 if (rc != 0) {
669 /*
670 * We were unable to send the oplock break request,
671 * presumably because the connection is gone.
672 * Close uninteresting handles.
673 */
674 lease_close_notconn(sr, NewLevel);
675 /* Note: some handles may remain on the lease. */
676 if (!AckReq)
677 return;
678 /* Do local Ack below. */
679 } else {
680 /*
681 * OK, we were able to send the break message.
682 * If no ack. required, we're done.
683 */
684 if (!AckReq)
685 return;
686
687 /*
688 * We're expecting an ACK. Wait in this thread
689 * so we can log clients that don't respond.
690 * Note: this can also fail for other reasons
691 * such as client disconnect or server shutdown.
692 */
693 status = smb_oplock_wait_ack(sr, NewLevel);
694 if (status == 0)
695 return;
696
697 DTRACE_PROBE2(wait__ack__failed, smb_request_t *, sr,
698 uint32_t, status);
699
700 /*
701 * Will do local ack below. Note, after timeout,
702 * do a break to none or "no caching" regardless
703 * of what the passed in cache level was.
704 * That means: clear all except GRANULAR.
705 */
706 NewLevel = OPLOCK_LEVEL_GRANULAR;
707 }
708
709 /*
710 * Do the ack locally.
711 *
712 * Find the ofile with the leased oplock
713 * (may have moved before we took locks)
714 */
715 smb_llist_enter(&node->n_ofile_list, RW_READER);
716 mutex_enter(&node->n_oplock.ol_mutex);
717
718 old_ofile = ofile;
719 sr->fid_ofile = NULL;
720 status = lease_find_oplock(sr, lease);
721 if (status != 0) {
722 /* put back old_ofile */
723 sr->fid_ofile = old_ofile;
724 goto unlock_out;
725 }
726 smb_llist_post(&node->n_ofile_list, old_ofile,
727 smb_ofile_release_LL);
728
729 ofile = sr->fid_ofile;
730
731 /*
732 * Now continue like the non-lease code
733 */
734 ofile->f_oplock.og_breaking = B_FALSE;
735 lease->ls_breaking = B_FALSE;
736 cv_broadcast(&lease->ls_ack_cv);
737
738 status = smb_oplock_ack_break(sr, ofile, &NewLevel);
739
740 ofile->f_oplock.og_state = NewLevel;
741 lease->ls_state = NewLevel;
742 /* ls_epoch does not change here */
743
744 if (ofile->dh_persist)
745 smb2_dh_update_oplock(sr, ofile);
746
747 unlock_out:
748 mutex_exit(&node->n_oplock.ol_mutex);
749 smb_llist_exit(&node->n_ofile_list);
750
751 #ifdef DEBUG
752 if (status != 0) {
753 cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x",
754 sr->session->ip_addr_str, status);
755 }
756 #endif
757 }
758
759 /*
760 * Client has an open handle and requests a lease.
761 * Convert SMB2 lease request info in to internal form,
762 * call common oplock code, convert result to SMB2.
763 *
764 * If necessary, "go async" here (at the end).
765 */
766 void
smb2_lease_acquire(smb_request_t * sr)767 smb2_lease_acquire(smb_request_t *sr)
768 {
769 smb_arg_open_t *op = &sr->arg.open;
770 smb_ofile_t *ofile = sr->fid_ofile;
771 smb_lease_t *lease = ofile->f_lease;
772 smb_node_t *node = ofile->f_node;
773 uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED;
774 uint32_t have, want; /* lease flags */
775 boolean_t NewGrant = B_FALSE;
776
777 /* Only disk trees get oplocks. */
778 ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE);
779
780 /*
781 * Only plain files (for now).
782 * Later, test SMB2_CAP_DIRECTORY_LEASING
783 */
784 if (!smb_node_is_file(ofile->f_node)) {
785 op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
786 return;
787 }
788
789 if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
790 op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
791 return;
792 }
793
794 /*
795 * SMB2: Convert to internal form.
796 * Caller should have setup the lease.
797 */
798 ASSERT(op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE);
799 ASSERT(lease != NULL);
800 if (lease == NULL) {
801 op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
802 return;
803 }
804 op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
805 (op->lease_state & CACHE_RWH);
806
807 /*
808 * Tree options may force shared oplocks,
809 * in which case we reduce the request.
810 */
811 if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
812 op->op_oplock_state &= ~WRITE_CACHING;
813 }
814
815 /*
816 * Using the "Locks Held" (LH) variant of smb_oplock_request
817 * below so things won't change underfoot.
818 */
819 smb_llist_enter(&node->n_ofile_list, RW_READER);
820 mutex_enter(&node->n_oplock.ol_mutex);
821
822 /*
823 * MS-SMB2 3.3.5.9.8 and 3.3.5.9.11 Lease (V2) create contexts
824 *
825 * If the caching state requested in LeaseState of the (create ctx)
826 * is not a superset of Lease.LeaseState or if Lease.Breaking is TRUE,
827 * the server MUST NOT promote Lease.LeaseState. If the lease state
828 * requested is a superset of Lease.LeaseState and Lease.Breaking is
829 * FALSE, the server MUST request promotion of the lease state from
830 * the underlying object store to the new caching state.
831 */
832 have = lease->ls_state & CACHE_RWH;
833 want = op->op_oplock_state & CACHE_RWH;
834 if ((have & ~want) != 0 || lease->ls_breaking) {
835 op->op_oplock_state = have |
836 OPLOCK_LEVEL_GRANULAR;
837 goto done;
838 }
839
840 /*
841 * Handle oplock requests in three parts:
842 * a: Requests with WRITE_CACHING
843 * b: Requests with HANDLE_CACHING
844 * c: Requests with READ_CACHING
845 * reducing the request before b and c.
846 *
847 * In each: first check if the lease grants the
848 * (possibly reduced) request, in which case we
849 * leave the lease unchanged and return what's
850 * granted by the lease. Otherwise, try to get
851 * the oplock, and if the succeeds, wait for any
852 * breaks, update the lease, and return.
853 */
854
855 /*
856 * Try exclusive (request is RW or RWH)
857 */
858 if ((op->op_oplock_state & WRITE_CACHING) != 0) {
859 /* Alread checked (want & ~have) */
860
861 status = smb_oplock_request_LH(sr, ofile,
862 &op->op_oplock_state);
863 if (status == NT_STATUS_SUCCESS ||
864 status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
865 NewGrant = B_TRUE;
866 goto done;
867 }
868
869 /*
870 * We did not get the exclusive oplock.
871 *
872 * There are odd rules about lease upgrade.
873 * If the existing lease grants R and the
874 * client fails to upgrade it to "RWH"
875 * (presumably due to handle conflicts)
876 * then just return the existing lease,
877 * even though upgrade to RH would work.
878 */
879 if (have != 0) {
880 op->op_oplock_state = have |
881 OPLOCK_LEVEL_GRANULAR;
882 goto done;
883 }
884
885 /*
886 * Keep trying without write.
887 * Need to re-init op_oplock_state
888 */
889 op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
890 (op->lease_state & CACHE_RH);
891 }
892
893 /*
894 * Try shared ("RH")
895 */
896 if ((op->op_oplock_state & HANDLE_CACHING) != 0) {
897 want = op->op_oplock_state & CACHE_RWH;
898 if ((want & ~have) == 0)
899 goto done;
900
901 status = smb_oplock_request_LH(sr, ofile,
902 &op->op_oplock_state);
903 if (status == NT_STATUS_SUCCESS ||
904 status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
905 NewGrant = B_TRUE;
906 goto done;
907 }
908
909 /*
910 * We did not get "RH", probably because
911 * ther were (old style) Level II oplocks.
912 * Continue, try for just read.
913 * Again, re-init op_oplock_state
914 */
915 op->op_oplock_state = OPLOCK_LEVEL_GRANULAR |
916 (op->lease_state & CACHE_R);
917 }
918
919 /*
920 * Try shared ("R")
921 */
922 if ((op->op_oplock_state & READ_CACHING) != 0) {
923 want = op->op_oplock_state & CACHE_RWH;
924 if ((want & ~have) == 0)
925 goto done;
926
927 status = smb_oplock_request_LH(sr, ofile,
928 &op->op_oplock_state);
929 if (status == NT_STATUS_SUCCESS ||
930 status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
931 NewGrant = B_TRUE;
932 goto done;
933 }
934
935 /*
936 * We did not get "R".
937 * Fall into "none".
938 */
939 }
940
941 /*
942 * None of the above were able to get an oplock.
943 * The lease has no caching rights, and we didn't
944 * add any in this request. Return it as-is.
945 */
946 op->op_oplock_state = OPLOCK_LEVEL_GRANULAR;
947
948 done:
949 /*
950 * Only success cases get here
951 */
952
953 /*
954 * Keep track of what we got (ofile->f_oplock.og_state etc)
955 * so we'll know what we had when sending a break later.
956 * Also keep a copy of some things in the lease.
957 *
958 * Not using og_dialect here, as ofile->f_lease tells us
959 * this has to be using granular oplocks.
960 */
961 if (NewGrant) {
962 ofile->f_oplock.og_state = op->op_oplock_state;
963 ofile->f_oplock.og_breakto = op->op_oplock_state;
964 ofile->f_oplock.og_breaking = B_FALSE;
965
966 lease->ls_oplock_ofile = ofile;
967 lease->ls_state = ofile->f_oplock.og_state;
968 lease->ls_breakto = ofile->f_oplock.og_breakto;
969 lease->ls_breaking = B_FALSE;
970 lease->ls_epoch++;
971
972 if (ofile->dh_persist) {
973 smb2_dh_update_oplock(sr, ofile);
974 }
975 }
976
977 /*
978 * Convert internal oplock state to SMB2
979 */
980 op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
981 op->lease_state = lease->ls_state & CACHE_RWH;
982 op->lease_flags = (lease->ls_breaking != 0) ?
983 SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0;
984 op->lease_epoch = lease->ls_epoch;
985 op->lease_version = lease->ls_version;
986
987 /*
988 * End of lock-held region
989 */
990 mutex_exit(&node->n_oplock.ol_mutex);
991 smb_llist_exit(&node->n_ofile_list);
992
993 /*
994 * After a new oplock grant, the status return
995 * may indicate we need to wait for breaks.
996 */
997 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
998 (void) smb2sr_go_async(sr);
999 (void) smb_oplock_wait_break(sr, ofile->f_node, 0);
1000 }
1001 }
1002
1003 /*
1004 * This ofile has a lease and is about to close.
1005 * Called by smb_ofile_close when there's a lease.
1006 *
1007 * Note that a client may close an ofile in response to an
1008 * oplock break or lease break intead of doing an Ack break,
1009 * so this must wake anything that might be waiting on an ack
1010 * when the last close of a lease happens.
1011 *
1012 * With leases, just one ofile on a lease owns the oplock.
1013 * If an ofile with a lease is closed and it's the one that
1014 * owns the oplock, try to move the oplock to another ofile
1015 * on the same lease.
1016 *
1017 * Would prefer that we could just use smb_ofile_hold_olbrk
1018 * to select a suitable destination for the move, but this
1019 * is called while holding the owning tree ofile list etc
1020 * which can cause deadlock as described in illumos 13850
1021 * when smb_ofile_hold_olbrk has to wait. XXX todo
1022 */
1023 void
smb2_lease_ofile_close(smb_ofile_t * ofile)1024 smb2_lease_ofile_close(smb_ofile_t *ofile)
1025 {
1026 smb_node_t *node = ofile->f_node;
1027 smb_lease_t *lease = ofile->f_lease;
1028 smb_ofile_t *o;
1029
1030 ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
1031 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
1032
1033 #ifdef DEBUG
1034 FOREACH_NODE_OFILE(node, o) {
1035 DTRACE_PROBE1(each_ofile, smb_ofile_t *, o);
1036 }
1037 #endif
1038
1039 /*
1040 * If this ofile was not the oplock owner for this lease,
1041 * we can leave things as they are.
1042 */
1043 if (lease->ls_oplock_ofile != ofile)
1044 return;
1045
1046 /*
1047 * Find another ofile to which we can move the oplock.
1048 * First try for one that's open. Usually find one.
1049 */
1050 FOREACH_NODE_OFILE(node, o) {
1051 if (o == ofile)
1052 continue;
1053 if (o->f_lease != lease)
1054 continue;
1055 if (o->f_oplock_closing)
1056 continue;
1057
1058 mutex_enter(&o->f_mutex);
1059 if (o->f_state == SMB_OFILE_STATE_OPEN) {
1060 smb_oplock_move(node, ofile, o);
1061 lease->ls_oplock_ofile = o;
1062 mutex_exit(&o->f_mutex);
1063 return;
1064 }
1065 mutex_exit(&o->f_mutex);
1066 }
1067
1068 /*
1069 * Now try for one that's orphaned etc.
1070 */
1071 FOREACH_NODE_OFILE(node, o) {
1072 if (o == ofile)
1073 continue;
1074 if (o->f_lease != lease)
1075 continue;
1076 if (o->f_oplock_closing)
1077 continue;
1078
1079 /*
1080 * Allow most states as seen in smb_ofile_hold_olbrk
1081 * without waiting for "_reconnect" or "_saving".
1082 * Skip "_expired" because that's about to close.
1083 * This is OK because just swapping the oplock state
1084 * between two ofiles does not interfere with the
1085 * dh_save or reconnect code paths.
1086 */
1087 mutex_enter(&o->f_mutex);
1088 switch (o->f_state) {
1089 case SMB_OFILE_STATE_OPEN:
1090 case SMB_OFILE_STATE_SAVE_DH:
1091 case SMB_OFILE_STATE_SAVING:
1092 case SMB_OFILE_STATE_ORPHANED:
1093 case SMB_OFILE_STATE_RECONNECT:
1094 smb_oplock_move(node, ofile, o);
1095 lease->ls_oplock_ofile = o;
1096 mutex_exit(&o->f_mutex);
1097 return;
1098 }
1099 mutex_exit(&o->f_mutex);
1100 }
1101
1102 /*
1103 * Normal for last close on a lease.
1104 * Wakeup ACK waiters too.
1105 */
1106 lease->ls_state = 0;
1107 lease->ls_breakto = 0;
1108 lease->ls_breaking = B_FALSE;
1109 cv_broadcast(&lease->ls_ack_cv);
1110
1111 lease->ls_oplock_ofile = NULL;
1112 }
1113