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 }