1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
14 * Copyright 2019 Joyent, Inc.
15 * Copyright 2022 RackTop Systems, Inc.
16 */
17
18 /*
19 * Test & debug program for oplocks
20 *
21 * This implements a simple command reader which accepts
22 * commands to simulate oplock events, and prints the
23 * state changes and actions that would happen after
24 * each event.
25 */
26
27 #include <sys/types.h>
28 #include <sys/debug.h>
29 #include <sys/stddef.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <unistd.h>
35
36 #include <smbsrv/smb_kproto.h>
37 #include <smbsrv/smb_oplock.h>
38
39 extern const char *xlate_nt_status(uint32_t);
40
41 #define OPLOCK_CACHE_RWH (READ_CACHING | HANDLE_CACHING | WRITE_CACHING)
42 #define OPLOCK_TYPE (LEVEL_TWO_OPLOCK | LEVEL_ONE_OPLOCK |\
43 BATCH_OPLOCK | OPLOCK_LEVEL_GRANULAR)
44
45 #define MAXFID 10
46
47 smb_node_t root_node, test_node;
48 smb_ofile_t ofile_array[MAXFID];
49 smb_request_t test_sr;
50 uint32_t last_ind_break_level;
51 char cmdbuf[100];
52
53 static void run_ind_break_in_ack(smb_ofile_t *);
54
55 #define BIT_DEF(name) { name, #name }
56
57 struct bit_defs {
58 uint32_t mask;
59 const char *name;
60 } state_bits[] = {
61 BIT_DEF(NO_OPLOCK),
62 BIT_DEF(BREAK_TO_NO_CACHING),
63 BIT_DEF(BREAK_TO_WRITE_CACHING),
64 BIT_DEF(BREAK_TO_HANDLE_CACHING),
65 BIT_DEF(BREAK_TO_READ_CACHING),
66 BIT_DEF(BREAK_TO_TWO_TO_NONE),
67 BIT_DEF(BREAK_TO_NONE),
68 BIT_DEF(BREAK_TO_TWO),
69 BIT_DEF(BATCH_OPLOCK),
70 BIT_DEF(LEVEL_ONE_OPLOCK),
71 BIT_DEF(LEVEL_TWO_OPLOCK),
72 BIT_DEF(MIXED_R_AND_RH),
73 BIT_DEF(EXCLUSIVE),
74 BIT_DEF(WRITE_CACHING),
75 BIT_DEF(HANDLE_CACHING),
76 BIT_DEF(READ_CACHING),
77 { 0, NULL }
78 };
79
80 /*
81 * Helper to print flags fields
82 */
83 static void
print_bits32(char * label,struct bit_defs * bit,uint32_t state)84 print_bits32(char *label, struct bit_defs *bit, uint32_t state)
85 {
86 printf("%s0x%x (", label, state);
87 while (bit->mask != 0) {
88 if ((state & bit->mask) != 0)
89 printf(" %s", bit->name);
90 bit++;
91 }
92 printf(" )\n");
93 }
94
95 /*
96 * Command language:
97 *
98 */
99 const char helpstr[] = "Commands:\n"
100 "help\t\tList commands\n"
101 "show\t\tShow OpLock state etc.\n"
102 "open FID\n"
103 "close FID\n"
104 "req FID [OplockLevel]\n"
105 "ack FID [OplockLevel]\n"
106 "brk-parent FID\n"
107 "brk-open [OverWrite]\n"
108 "brk-handle FID\n"
109 "brk-read FID\n"
110 "brk-write FID\n"
111 "brk-setinfo FID [InfoClass]\n"
112 "move FID1 FID2\n"
113 "waiters FID [count]\n";
114
115 /*
116 * Command handlers
117 */
118
119 static void
do_show(void)120 do_show(void)
121 {
122 smb_node_t *node = &test_node;
123 smb_oplock_t *ol = &node->n_oplock;
124 uint32_t state = ol->ol_state;
125 smb_ofile_t *f;
126
127 print_bits32(" ol_state=", state_bits, state);
128
129 if (ol->excl_open != NULL)
130 printf(" Excl=Y (FID=%d)", ol->excl_open->f_fid);
131 else
132 printf(" Excl=n");
133 printf(" cnt_II=%d cnt_R=%d cnt_RH=%d cnt_RHBQ=%d\n",
134 ol->cnt_II, ol->cnt_R, ol->cnt_RH, ol->cnt_RHBQ);
135
136 printf(" ofile_cnt=%d\n", node->n_ofile_list.ll_count);
137 FOREACH_NODE_OFILE(node, f) {
138 smb_oplock_grant_t *og = &f->f_oplock;
139 printf(" fid=%d Lease=%s State=0x%x",
140 f->f_fid,
141 f->TargetOplockKey, /* lease */
142 og->og_state);
143 if (og->og_breaking)
144 printf(" BreakTo=0x%x", og->og_breakto);
145 printf(" Excl=%s onlist:",
146 (ol->excl_open == f) ? "Y" : "N");
147 if (og->onlist_II)
148 printf(" II");
149 if (og->onlist_R)
150 printf(" R");
151 if (og->onlist_RH)
152 printf(" RH");
153 if (og->onlist_RHBQ) {
154 printf(" RHBQ(to %s)",
155 og->BreakingToRead ?
156 "read" : "none");
157 }
158 printf("\n");
159 }
160 }
161
162 static void
do_open(int fid,char * arg2)163 do_open(int fid, char *arg2)
164 {
165 smb_node_t *node = &test_node;
166 smb_ofile_t *ofile = &ofile_array[fid];
167
168 /*
169 * Simulate an open (minimal init)
170 */
171 if (ofile->f_refcnt) {
172 printf("open fid %d already opened\n");
173 return;
174 }
175
176 if (arg2 != NULL) {
177 (void) strlcpy((char *)ofile->TargetOplockKey, arg2,
178 SMB_LEASE_KEY_SZ);
179 }
180
181 ofile->f_refcnt++;
182 node->n_open_count++;
183 smb_llist_insert_tail(&node->n_ofile_list, ofile);
184 printf(" open %d OK\n", fid);
185 }
186
187 static void
do_close(int fid)188 do_close(int fid)
189 {
190 smb_node_t *node = &test_node;
191 smb_ofile_t *ofile = &ofile_array[fid];
192
193 /*
194 * Simulate an close
195 */
196 if (ofile->f_refcnt <= 0) {
197 printf(" close fid %d already closed\n");
198 return;
199 }
200
201 smb_llist_enter(&node->n_ofile_list, RW_READER);
202 mutex_enter(&node->n_oplock.ol_mutex);
203
204 smb_oplock_break_CLOSE(ofile->f_node, ofile);
205
206 smb_llist_remove(&node->n_ofile_list, ofile);
207 node->n_open_count--;
208
209 mutex_exit(&node->n_oplock.ol_mutex);
210 smb_llist_exit(&node->n_ofile_list);
211
212 ofile->f_refcnt--;
213
214 bzero(ofile->TargetOplockKey, SMB_LEASE_KEY_SZ);
215
216 printf(" close OK\n");
217 }
218
219 static void
do_req(int fid,char * arg2)220 do_req(int fid, char *arg2)
221 {
222 smb_ofile_t *ofile = &ofile_array[fid];
223 uint32_t oplock = BATCH_OPLOCK;
224 uint32_t status;
225
226 if (arg2 != NULL)
227 oplock = strtol(arg2, NULL, 16);
228
229 /*
230 * Request an oplock
231 */
232 status = smb_oplock_request(&test_sr, ofile, &oplock);
233 if (status == 0 ||
234 status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
235 ofile->f_oplock.og_state = oplock;
236 /* When no break pending, breakto=state */
237 ofile->f_oplock.og_breakto = oplock;
238 ofile->f_oplock.og_breaking = B_FALSE;
239 }
240 printf(" req oplock fid=%d ret oplock=0x%x status=0x%x (%s)\n",
241 fid, oplock, status, xlate_nt_status(status));
242 }
243
244
245 static void
do_ack(int fid,char * arg2)246 do_ack(int fid, char *arg2)
247 {
248 smb_node_t *node = &test_node;
249 smb_ofile_t *ofile = &ofile_array[fid];
250 uint32_t oplock;
251 uint32_t status;
252
253 /* Default to level in last smb_oplock_ind_break() */
254 oplock = last_ind_break_level;
255 if (arg2 != NULL)
256 oplock = strtol(arg2, NULL, 16);
257
258 smb_llist_enter(&node->n_ofile_list, RW_READER);
259 mutex_enter(&node->n_oplock.ol_mutex);
260
261 ofile->f_oplock.og_breaking = 0;
262 status = smb_oplock_ack_break(&test_sr, ofile, &oplock);
263 if (status == 0)
264 ofile->f_oplock.og_state = oplock;
265
266 mutex_exit(&node->n_oplock.ol_mutex);
267 smb_llist_exit(&node->n_ofile_list);
268
269 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
270 /* should not get this status */
271 printf(" ack: break fid=%d, break-in-progress\n", fid);
272 ASSERT(0);
273 }
274
275 printf(" ack: break fid=%d, newstate=0x%x, status=0x%x (%s)\n",
276 fid, oplock, status, xlate_nt_status(status));
277
278 run_ind_break_in_ack(ofile);
279 }
280
281 static void
do_brk_parent(int fid)282 do_brk_parent(int fid)
283 {
284 smb_ofile_t *ofile = &ofile_array[fid];
285 uint32_t status;
286
287 status = smb_oplock_break_PARENT(&test_node, ofile);
288 printf(" brk-parent %d ret status=0x%x (%s)\n",
289 fid, status, xlate_nt_status(status));
290 }
291
292 static void
do_brk_open(int fid,char * arg2)293 do_brk_open(int fid, char *arg2)
294 {
295 smb_ofile_t *ofile = &ofile_array[fid];
296 uint32_t status;
297 int disp = FILE_OPEN;
298
299 if (arg2 != NULL)
300 disp = strtol(arg2, NULL, 16);
301
302 status = smb_oplock_break_OPEN(&test_node, ofile, 7, disp);
303 printf(" brk-open %d ret status=0x%x (%s)\n",
304 fid, status, xlate_nt_status(status));
305 }
306
307 static void
do_brk_handle(int fid)308 do_brk_handle(int fid)
309 {
310 smb_ofile_t *ofile = &ofile_array[fid];
311 uint32_t status;
312
313 status = smb_oplock_break_HANDLE(&test_node, ofile);
314 printf(" brk-handle %d ret status=0x%x (%s)\n",
315 fid, status, xlate_nt_status(status));
316
317 }
318
319 static void
do_brk_read(int fid)320 do_brk_read(int fid)
321 {
322 smb_ofile_t *ofile = &ofile_array[fid];
323 uint32_t status;
324
325 status = smb_oplock_break_READ(ofile->f_node, ofile);
326 printf(" brk-read %d ret status=0x%x (%s)\n",
327 fid, status, xlate_nt_status(status));
328 }
329
330 static void
do_brk_write(int fid)331 do_brk_write(int fid)
332 {
333 smb_ofile_t *ofile = &ofile_array[fid];
334 uint32_t status;
335
336 status = smb_oplock_break_WRITE(ofile->f_node, ofile);
337 printf(" brk-write %d ret status=0x%x (%s)\n",
338 fid, status, xlate_nt_status(status));
339 }
340
341 static void
do_brk_setinfo(int fid,char * arg2)342 do_brk_setinfo(int fid, char *arg2)
343 {
344 smb_ofile_t *ofile = &ofile_array[fid];
345 uint32_t status;
346 int infoclass = FileEndOfFileInformation; /* 20 */
347
348 if (arg2 != NULL)
349 infoclass = strtol(arg2, NULL, 16);
350
351 status = smb_oplock_break_SETINFO(
352 &test_node, ofile, infoclass);
353 printf(" brk-setinfo %d 0x%x ret status=0x%x (%s)\n",
354 fid, infoclass, status, xlate_nt_status(status));
355
356 }
357
358 /*
359 * Move oplock to another FD, as specified,
360 * or any other available open
361 */
362 static void
do_move(int fid,char * arg2)363 do_move(int fid, char *arg2)
364 {
365 smb_node_t *node = &test_node;
366 smb_ofile_t *ofile = &ofile_array[fid];
367 smb_ofile_t *of2;
368 int fid2;
369
370 if (arg2 == NULL) {
371 fprintf(stderr, "move: FID2 required\n");
372 return;
373 }
374 fid2 = atoi(arg2);
375 if (fid2 <= 0 || fid2 >= MAXFID) {
376 fprintf(stderr, "move: bad FID2 %d\n", fid2);
377 return;
378 }
379 of2 = &ofile_array[fid2];
380
381 mutex_enter(&node->n_oplock.ol_mutex);
382
383 smb_oplock_move(&test_node, ofile, of2);
384
385 mutex_exit(&node->n_oplock.ol_mutex);
386
387 printf(" move %d %d\n", fid, fid2);
388 }
389
390 /*
391 * Set/clear oplock.waiters, which affects ack-break
392 */
393 static void
do_waiters(int fid,char * arg2)394 do_waiters(int fid, char *arg2)
395 {
396 smb_node_t *node = &test_node;
397 smb_oplock_t *ol = &node->n_oplock;
398 int old, new = 0;
399
400 if (arg2 != NULL)
401 new = atoi(arg2);
402
403 old = ol->waiters;
404 ol->waiters = new;
405
406 printf(" waiters %d -> %d\n", old, new);
407 }
408
409 int
main(int argc,char * argv[])410 main(int argc, char *argv[])
411 {
412 smb_node_t *node = &test_node;
413 char *cmd;
414 char *arg1;
415 char *arg2;
416 char *savep;
417 char *sep = " \t\n";
418 char *prompt = NULL;
419 int fid;
420
421 if (isatty(0))
422 prompt = "> ";
423
424 mutex_init(&node->n_mutex, NULL, MUTEX_DEFAULT, NULL);
425
426 smb_llist_constructor(&node->n_ofile_list, sizeof (smb_ofile_t),
427 offsetof(smb_ofile_t, f_node_lnd));
428
429 for (fid = 0; fid < MAXFID; fid++) {
430 smb_ofile_t *f = &ofile_array[fid];
431
432 f->f_magic = SMB_OFILE_MAGIC;
433 mutex_init(&f->f_mutex, NULL, MUTEX_DEFAULT, NULL);
434 f->f_fid = fid;
435 f->f_ftype = SMB_FTYPE_DISK;
436 f->f_node = &test_node;
437 }
438
439 for (;;) {
440 if (prompt) {
441 (void) fputs(prompt, stdout);
442 fflush(stdout);
443 }
444
445 cmd = fgets(cmdbuf, sizeof (cmdbuf), stdin);
446 if (cmd == NULL)
447 break;
448 if (cmd[0] == '#')
449 continue;
450
451 if (prompt == NULL) {
452 /* Put commands in the output too. */
453 (void) fputs(cmdbuf, stdout);
454 }
455 cmd = strtok_r(cmd, sep, &savep);
456 if (cmd == NULL)
457 continue;
458
459 /*
460 * Commands with no args
461 */
462 if (0 == strcmp(cmd, "help")) {
463 (void) fputs(helpstr, stdout);
464 continue;
465 }
466
467 if (0 == strcmp(cmd, "show")) {
468 do_show();
469 continue;
470 }
471
472 /*
473 * Commands with one arg (the FID)
474 */
475 arg1 = strtok_r(NULL, sep, &savep);
476 if (arg1 == NULL) {
477 fprintf(stderr, "%s missing arg1\n", cmd);
478 continue;
479 }
480 fid = atoi(arg1);
481 if (fid <= 0 || fid >= MAXFID) {
482 fprintf(stderr, "%s bad FID %d\n", cmd, fid);
483 continue;
484 }
485
486 if (0 == strcmp(cmd, "close")) {
487 do_close(fid);
488 continue;
489 }
490 if (0 == strcmp(cmd, "brk-parent")) {
491 do_brk_parent(fid);
492 continue;
493 }
494 if (0 == strcmp(cmd, "brk-handle")) {
495 do_brk_handle(fid);
496 continue;
497 }
498 if (0 == strcmp(cmd, "brk-read")) {
499 do_brk_read(fid);
500 continue;
501 }
502 if (0 == strcmp(cmd, "brk-write")) {
503 do_brk_write(fid);
504 continue;
505 }
506
507 /*
508 * Commands with an (optional) arg2.
509 */
510 arg2 = strtok_r(NULL, sep, &savep);
511
512 if (0 == strcmp(cmd, "open")) {
513 do_open(fid, arg2);
514 continue;
515 }
516 if (0 == strcmp(cmd, "req")) {
517 do_req(fid, arg2);
518 continue;
519 }
520 if (0 == strcmp(cmd, "ack")) {
521 do_ack(fid, arg2);
522 continue;
523 }
524 if (0 == strcmp(cmd, "brk-open")) {
525 do_brk_open(fid, arg2);
526 continue;
527 }
528 if (0 == strcmp(cmd, "brk-setinfo")) {
529 do_brk_setinfo(fid, arg2);
530 continue;
531 }
532 if (0 == strcmp(cmd, "move")) {
533 do_move(fid, arg2);
534 continue;
535 }
536 if (0 == strcmp(cmd, "waiters")) {
537 do_waiters(fid, arg2);
538 continue;
539 }
540
541 fprintf(stderr, "%s unknown command. Try help\n", cmd);
542 }
543 return (0);
544 }
545
546 /*
547 * A few functions called by the oplock code
548 * Stubbed out, and/or just print a message.
549 */
550
551 boolean_t
smb_node_is_file(smb_node_t * node)552 smb_node_is_file(smb_node_t *node)
553 {
554 return (B_TRUE);
555 }
556
557 boolean_t
smb_ofile_is_open(smb_ofile_t * ofile)558 smb_ofile_is_open(smb_ofile_t *ofile)
559 {
560 return (ofile->f_refcnt != 0);
561 }
562
563 int
smb_lock_range_access(smb_request_t * sr,smb_node_t * node,uint64_t start,uint64_t length,boolean_t will_write)564 smb_lock_range_access(
565 smb_request_t *sr,
566 smb_node_t *node,
567 uint64_t start,
568 uint64_t length,
569 boolean_t will_write)
570 {
571 return (0);
572 }
573
574 /*
575 * Test code replacement for combination of:
576 * smb_oplock_hdl_update()
577 * smb_oplock_send_break()
578 *
579 * In a real server, we would send a break to the client,
580 * and keep track (at the SMB level) whether this oplock
581 * was obtained via a lease or an old-style oplock.
582 */
583 static void
test_oplock_send_break(smb_ofile_t * ofile,uint32_t NewLevel,boolean_t AckReq)584 test_oplock_send_break(smb_ofile_t *ofile,
585 uint32_t NewLevel, boolean_t AckReq)
586 {
587 smb_oplock_grant_t *og = &ofile->f_oplock;
588 uint32_t OldLevel;
589
590 /* Skip building a message. */
591
592 if ((og->og_state & OPLOCK_LEVEL_GRANULAR) != 0)
593 NewLevel |= OPLOCK_LEVEL_GRANULAR;
594
595 OldLevel = og->og_state;
596 og->og_breakto = NewLevel;
597 og->og_breaking = B_TRUE;
598
599 printf("*smb_oplock_send_break fid=%d "
600 "NewLevel=0x%x, OldLevel=0x%x, AckReq=%d)\n",
601 ofile->f_fid, NewLevel, OldLevel, AckReq);
602
603 if (!AckReq) {
604 og->og_state = NewLevel;
605 og->og_breaking = B_FALSE;
606 }
607
608 /* Next, smb_oplock_send_break() would send a break. */
609 last_ind_break_level = NewLevel;
610 }
611
612 /*
613 * Simplified version of what's in smb_srv_oplock.c
614 */
615 void
smb_oplock_ind_break(smb_ofile_t * ofile,uint32_t NewLevel,boolean_t AckReq,uint32_t status)616 smb_oplock_ind_break(smb_ofile_t *ofile, uint32_t NewLevel,
617 boolean_t AckReq, uint32_t status)
618 {
619 smb_oplock_grant_t *og = &ofile->f_oplock;
620
621 printf("*smb_oplock_ind_break fid=%d NewLevel=0x%x,"
622 " AckReq=%d, ComplStatus=0x%x (%s)\n",
623 ofile->f_fid, NewLevel, AckReq,
624 status, xlate_nt_status(status));
625
626 /*
627 * Note that the CompletionStatus from the FS level
628 * (smb_cmn_oplock.c) encodes what kind of action we
629 * need to take at the SMB level.
630 */
631 switch (status) {
632
633 case NT_STATUS_SUCCESS:
634 case NT_STATUS_CANNOT_GRANT_REQUESTED_OPLOCK:
635 test_oplock_send_break(ofile, NewLevel, AckReq);
636 break;
637
638 case NT_STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE:
639 case NT_STATUS_OPLOCK_HANDLE_CLOSED:
640 og->og_state = OPLOCK_LEVEL_NONE;
641 og->og_breakto = OPLOCK_LEVEL_NONE;
642 og->og_breaking = B_FALSE;
643 break;
644
645 default:
646 ASSERT(0);
647 break;
648 }
649 }
650
651 /* Arrange for break_in_ack to run after ack completes. */
652 static uint32_t break_in_ack_NewLevel;
653 static boolean_t break_in_ack_AckReq;
654 static boolean_t break_in_ack_called;
655
656 void
smb_oplock_ind_break_in_ack(smb_request_t * sr,smb_ofile_t * ofile,uint32_t NewLevel,boolean_t AckRequired)657 smb_oplock_ind_break_in_ack(smb_request_t *sr, smb_ofile_t *ofile,
658 uint32_t NewLevel, boolean_t AckRequired)
659 {
660 ASSERT(sr == &test_sr);
661
662 /* Process these after ack */
663 ASSERT(!break_in_ack_called);
664 break_in_ack_called = B_TRUE;
665 break_in_ack_NewLevel = NewLevel;
666 break_in_ack_AckReq = AckRequired;
667 }
668
669 static void
run_ind_break_in_ack(smb_ofile_t * ofile)670 run_ind_break_in_ack(smb_ofile_t *ofile)
671 {
672 uint32_t NewLevel;
673 boolean_t AckReq;
674
675 /* Process these after ack */
676 if (!break_in_ack_called)
677 return;
678 break_in_ack_called = B_FALSE;
679 NewLevel = break_in_ack_NewLevel;
680 AckReq = break_in_ack_AckReq;
681
682 printf("*smb_oplock_ind_break_in_ack fid=%d NewLevel=0x%x,"
683 " AckReq=%d\n",
684 ofile->f_fid, NewLevel, AckReq);
685
686 test_oplock_send_break(ofile, NewLevel, AckReq);
687 }
688
689 uint32_t
smb_oplock_wait_break(smb_request_t * sr,smb_node_t * node,int timeout)690 smb_oplock_wait_break(smb_request_t *sr, smb_node_t *node, int timeout)
691 {
692 printf("*smb_oplock_wait_break (state=0x%x)\n",
693 node->n_oplock.ol_state);
694 return (0);
695 }
696
697 int
smb_fem_oplock_install(smb_node_t * node)698 smb_fem_oplock_install(smb_node_t *node)
699 {
700 return (0);
701 }
702
703 void
smb_fem_oplock_uninstall(smb_node_t * node)704 smb_fem_oplock_uninstall(smb_node_t *node)
705 {
706 }
707
708 /*
709 * There are a couple DTRACE_PROBE* in smb_cmn_oplock.c but we're
710 * not linking with the user-level dtrace support, so just
711 * stub these out.
712 */
713 void
__dtrace_fksmb___probe1(char * n,unsigned long a)714 __dtrace_fksmb___probe1(char *n, unsigned long a)
715 {
716 }
717 void
__dtrace_fksmb___probe2(char * n,unsigned long a,unsigned long b)718 __dtrace_fksmb___probe2(char *n, unsigned long a, unsigned long b)
719 {
720 }
721