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 }