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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 552 smb_node_is_file(smb_node_t *node) 553 { 554 return (B_TRUE); 555 } 556 557 boolean_t 558 smb_ofile_is_open(smb_ofile_t *ofile) 559 { 560 return (ofile->f_refcnt != 0); 561 } 562 563 int 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 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 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 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 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 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 698 smb_fem_oplock_install(smb_node_t *node) 699 { 700 return (0); 701 } 702 703 void 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 714 __dtrace_fksmb___probe1(char *n, unsigned long a) 715 { 716 } 717 void 718 __dtrace_fksmb___probe2(char *n, unsigned long a, unsigned long b) 719 { 720 } 721