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