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