Print this page
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-1643 dtrace provider for smbsrv
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-5586 SMB2 ofiles need real Persistent IDs
NEX-5313 SMB2 oplock break notification should use TID=0
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-5560 smb2 should use 64-bit server-global uids
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-1892 30 sec. delay responding to smb2_create
NEX-1734 SMB2 oplock break request missing a flag
SMB-122 smbd core dumps in smbd_dc_update / smb_log
SMB-117 Win7 fails to open security properties
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/fs/smbsrv/smb2_oplock.c
          +++ new/usr/src/uts/common/fs/smbsrv/smb2_oplock.c
↓ open down ↓ 2 lines elided ↑ open up ↑
   3    3   * Common Development and Distribution License ("CDDL"), version 1.0.
   4    4   * You may only use this file in accordance with the terms of version
   5    5   * 1.0 of the CDDL.
   6    6   *
   7    7   * A full copy of the text of the CDDL should have accompanied this
   8    8   * source.  A copy of the CDDL is also available via the Internet at
   9    9   * http://www.illumos.org/license/CDDL.
  10   10   */
  11   11  
  12   12  /*
  13      - * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
       13 + * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  14   14   */
  15   15  
  16   16  /*
  17   17   * Dispatch function for SMB2_OPLOCK_BREAK
  18   18   */
  19   19  
  20   20  #include <smbsrv/smb2_kproto.h>
  21   21  
       22 +#define BATCH_OR_EXCL   (OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE)
       23 +
       24 +/* StructSize for the two "break" message formats. */
       25 +#define SSZ_OPLOCK      24
       26 +#define SSZ_LEASE       36
       27 +
  22   28  /*
  23   29   * SMB2 Oplock Break Acknowledgement
  24      - * [MS-SMB2 2.2.24]
       30 + * [MS-SMB2] 3.3.5.22.1 Processing an Oplock Acknowledgment
       31 + * Called via smb2_disp_table[]
  25   32   */
  26   33  smb_sdrc_t
  27   34  smb2_oplock_break_ack(smb_request_t *sr)
  28   35  {
  29      -        smb_node_t *node;
       36 +        smb_ofile_t *ofile;
  30   37          smb2fid_t smb2fid;
  31   38          uint32_t status;
  32      -        uint16_t StructSize;
  33      -        uint8_t OplockLevel;
  34      -        uint8_t brk;
       39 +        uint32_t NewLevel;
       40 +        uint8_t smbOplockLevel;
  35   41          int rc = 0;
       42 +        uint16_t StructSize;
  36   43  
  37   44          /*
  38      -         * Decode the SMB2 Oplock Break Ack.
       45 +         * Decode the SMB2 Oplock Break Ack (24 bytes) or
       46 +         * Lease Break Ack (36 bytes), starting with just
       47 +         * the StructSize, which tells us what this is.
  39   48           */
       49 +        rc = smb_mbc_decodef(&sr->smb_data, "w", &StructSize);
       50 +        if (rc != 0)
       51 +                return (SDRC_ERROR);
       52 +
       53 +        if (StructSize == SSZ_LEASE) {
       54 +                /* See smb2_lease.c */
       55 +                return (smb2_lease_break_ack(sr));
       56 +        }
       57 +        if (StructSize != SSZ_OPLOCK)
       58 +                return (SDRC_ERROR);
       59 +
       60 +        /*
       61 +         * Decode an SMB2 Oplock Break Ack.
       62 +         * [MS-SMB2] 2.2.24.1
       63 +         * Note: Struct size decoded above.
       64 +         */
  40   65          rc = smb_mbc_decodef(
  41      -            &sr->smb_data, "wb5.qq",
  42      -            &StructSize,                /* w */
  43      -            &OplockLevel,               /* b */
       66 +            &sr->smb_data, "b5.qq",
       67 +            &smbOplockLevel,            /* b */
  44   68              /* reserved                   5. */
  45   69              &smb2fid.persistent,        /* q */
  46   70              &smb2fid.temporal);         /* q */
  47      -        if (rc || StructSize != 24)
       71 +        if (rc != 0)
  48   72                  return (SDRC_ERROR);
  49   73  
       74 +        /* Find the ofile */
  50   75          status = smb2sr_lookup_fid(sr, &smb2fid);
  51      -        if (status)
       76 +        /* Success or NT_STATUS_FILE_CLOSED */
       77 +
       78 +        DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr);
       79 +        if (status != 0)
  52   80                  goto errout;
  53      -        if ((node = sr->fid_ofile->f_node) == NULL) {
  54      -                /* Not a regular file */
  55      -                status = NT_STATUS_INVALID_PARAMETER;
  56      -                goto errout;
  57      -        }
  58   81  
  59   82          /*
  60      -         * Process the oplock break ack.  We only expect levels
  61      -         * at or below the hightest break levels we send, which is
  62      -         * currently SMB2_OPLOCK_LEVEL_II.
       83 +         * Process an (old-style) oplock break ack.
  63   84           */
  64      -        switch (OplockLevel) {
       85 +        switch (smbOplockLevel) {
  65   86          case SMB2_OPLOCK_LEVEL_NONE:    /* 0x00 */
  66      -                brk = SMB_OPLOCK_BREAK_TO_NONE;
       87 +                NewLevel = OPLOCK_LEVEL_NONE;
  67   88                  break;
  68      -
  69   89          case SMB2_OPLOCK_LEVEL_II:      /* 0x01 */
  70      -                brk = SMB_OPLOCK_BREAK_TO_LEVEL_II;
       90 +                NewLevel = OPLOCK_LEVEL_TWO;
  71   91                  break;
  72      -
  73      -        /* We don't break to these levels (yet). */
  74   92          case SMB2_OPLOCK_LEVEL_EXCLUSIVE: /* 0x08 */
       93 +                NewLevel = OPLOCK_LEVEL_ONE;
       94 +                break;
  75   95          case SMB2_OPLOCK_LEVEL_BATCH:   /* 0x09 */
       96 +                NewLevel = OPLOCK_LEVEL_BATCH;
       97 +                break;
  76   98          case SMB2_OPLOCK_LEVEL_LEASE:   /* 0xFF */
  77      -        default: /* gcc -Wuninitialized */
  78      -                status = NT_STATUS_INVALID_PARAMETER;
       99 +        default:
      100 +                NewLevel = OPLOCK_LEVEL_NONE;
      101 +                break;
      102 +        }
      103 +
      104 +        ofile = sr->fid_ofile;
      105 +        ofile->f_oplock.og_breaking = 0;
      106 +        status = smb_oplock_ack_break(sr, ofile, &NewLevel);
      107 +        if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
      108 +                status = smb2sr_go_async(sr);
      109 +                if (status != 0)
      110 +                        goto errout;
      111 +                (void) smb_oplock_wait_break(ofile->f_node, 0);
      112 +                status = 0;
      113 +        }
      114 +        if (status != 0) {
      115 +                NewLevel = OPLOCK_LEVEL_NONE;
  79  116                  goto errout;
  80  117          }
  81  118  
  82      -        smb_oplock_ack(node, sr->fid_ofile, brk);
      119 +        ofile->f_oplock.og_state = NewLevel;
      120 +        switch (NewLevel & OPLOCK_LEVEL_TYPE_MASK) {
      121 +        case OPLOCK_LEVEL_NONE:
      122 +                smbOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
      123 +                break;
      124 +        case OPLOCK_LEVEL_TWO:
      125 +                smbOplockLevel = SMB2_OPLOCK_LEVEL_II;
      126 +                break;
      127 +        case OPLOCK_LEVEL_ONE:
      128 +                smbOplockLevel = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
      129 +                break;
      130 +        case OPLOCK_LEVEL_BATCH:
      131 +                smbOplockLevel = SMB2_OPLOCK_LEVEL_BATCH;
      132 +                break;
      133 +        case OPLOCK_LEVEL_GRANULAR:
      134 +        default:
      135 +                smbOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
      136 +                break;
      137 +        }
  83  138  
      139 +errout:
      140 +        sr->smb2_status = status;
      141 +        DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr);
      142 +        if (status) {
      143 +                smb2sr_put_error(sr, status);
      144 +                return (SDRC_SUCCESS);
      145 +        }
      146 +
  84  147          /*
  85      -         * Generate SMB2 Oplock Break response
  86      -         * [MS-SMB2] 2.2.25
      148 +         * Encode an SMB2 Oplock Break Ack response
      149 +         * [MS-SMB2] 2.2.25.1
  87  150           */
  88      -        StructSize = 24;
  89  151          (void) smb_mbc_encodef(
  90  152              &sr->reply, "wb5.qq",
  91      -            StructSize,                 /* w */
  92      -            OplockLevel,                /* b */
      153 +            SSZ_OPLOCK,                 /* w */
      154 +            smbOplockLevel,             /* b */
  93  155              /* reserved                   5. */
  94  156              smb2fid.persistent,         /* q */
  95  157              smb2fid.temporal);          /* q */
  96      -        return (SDRC_SUCCESS);
  97  158  
  98      -errout:
  99      -        smb2sr_put_error(sr, status);
 100  159          return (SDRC_SUCCESS);
 101  160  }
 102  161  
 103  162  /*
 104  163   * Compose an SMB2 Oplock Break Notification packet, including
 105  164   * the SMB2 header and everything, in sr->reply.
 106  165   * The caller will send it and free the request.
 107  166   */
 108  167  void
 109      -smb2_oplock_break_notification(smb_request_t *sr, uint8_t brk)
      168 +smb2_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel)
 110  169  {
 111  170          smb_ofile_t *ofile = sr->fid_ofile;
 112  171          smb2fid_t smb2fid;
 113  172          uint16_t StructSize;
 114  173          uint8_t OplockLevel;
 115  174  
 116      -        switch (brk) {
      175 +        /*
      176 +         * Convert internal level to SMB2
      177 +         */
      178 +        switch (NewLevel) {
 117  179          default:
 118  180                  ASSERT(0);
 119  181                  /* FALLTHROUGH */
 120      -        case SMB_OPLOCK_BREAK_TO_NONE:
      182 +        case OPLOCK_LEVEL_NONE:
 121  183                  OplockLevel = SMB2_OPLOCK_LEVEL_NONE;
 122  184                  break;
 123      -        case SMB_OPLOCK_BREAK_TO_LEVEL_II:
      185 +        case OPLOCK_LEVEL_TWO:
 124  186                  OplockLevel = SMB2_OPLOCK_LEVEL_II;
 125  187                  break;
 126  188          }
 127  189  
 128  190          /*
 129  191           * SMB2 Header
 130  192           */
 131  193          sr->smb2_cmd_code = SMB2_OPLOCK_BREAK;
 132  194          sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR;
 133      -        sr->smb_tid = ofile->f_tree->t_tid;
      195 +        sr->smb_tid = 0;
 134  196          sr->smb_pid = 0;
 135      -        sr->smb_uid = 0;
      197 +        sr->smb2_ssnid = 0;
 136  198          sr->smb2_messageid = UINT64_MAX;
 137  199          (void) smb2_encode_header(sr, B_FALSE);
 138  200  
 139  201          /*
 140  202           * SMB2 Oplock Break, variable part
 141  203           */
 142  204          StructSize = 24;
 143      -        smb2fid.persistent = 0;
      205 +        smb2fid.persistent = ofile->f_persistid;
 144  206          smb2fid.temporal = ofile->f_fid;
 145  207          (void) smb_mbc_encodef(
 146  208              &sr->reply, "wb5.qq",
 147  209              StructSize,         /* w */
 148  210              OplockLevel,        /* b */
 149  211              /* reserved           5. */
 150  212              smb2fid.persistent, /* q */
 151  213              smb2fid.temporal);  /* q */
      214 +}
      215 +
      216 +/*
      217 + * Client has an open handle and requests an oplock.
      218 + * Convert SMB2 oplock request info in to internal form,
      219 + * call common oplock code, convert result to SMB2.
      220 + *
      221 + * If necessary, "go async" here.
      222 + */
      223 +void
      224 +smb2_oplock_acquire(smb_request_t *sr)
      225 +{
      226 +        smb_arg_open_t *op = &sr->arg.open;
      227 +        smb_ofile_t *ofile = sr->fid_ofile;
      228 +        uint32_t status;
      229 +
      230 +        /* Only disk trees get oplocks. */
      231 +        ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE);
      232 +
      233 +        /* Only plain files... */
      234 +        if (!smb_node_is_file(ofile->f_node)) {
      235 +                op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
      236 +                return;
      237 +        }
      238 +
      239 +        if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
      240 +                op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
      241 +                return;
      242 +        }
      243 +
      244 +        /*
      245 +         * SMB2: Convert to internal form.
      246 +         */
      247 +        switch (op->op_oplock_level) {
      248 +        case SMB2_OPLOCK_LEVEL_BATCH:
      249 +                op->op_oplock_state = OPLOCK_LEVEL_BATCH;
      250 +                break;
      251 +        case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
      252 +                op->op_oplock_state = OPLOCK_LEVEL_ONE;
      253 +                break;
      254 +        case SMB2_OPLOCK_LEVEL_II:
      255 +                op->op_oplock_state = OPLOCK_LEVEL_TWO;
      256 +                break;
      257 +        case SMB2_OPLOCK_LEVEL_LEASE:
      258 +                ASSERT(0); /* Handled elsewhere */
      259 +                /* FALLTHROUGH */
      260 +        case SMB2_OPLOCK_LEVEL_NONE:
      261 +        default:
      262 +                op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
      263 +                return;
      264 +        }
      265 +
      266 +        /*
      267 +         * Tree options may force shared oplocks,
      268 +         * in which case we reduce the request.
      269 +         */
      270 +        if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
      271 +                op->op_oplock_state = OPLOCK_LEVEL_TWO;
      272 +        }
      273 +
      274 +        /*
      275 +         * Try exclusive first, if requested
      276 +         */
      277 +        if ((op->op_oplock_state & BATCH_OR_EXCL) != 0) {
      278 +                status = smb_oplock_request(sr, ofile,
      279 +                    &op->op_oplock_state);
      280 +        } else {
      281 +                status = NT_STATUS_OPLOCK_NOT_GRANTED;
      282 +        }
      283 +
      284 +        /*
      285 +         * If exclusive failed (or the tree forced shared oplocks)
      286 +         * try for a shared oplock (Level II)
      287 +         */
      288 +        if (status == NT_STATUS_OPLOCK_NOT_GRANTED) {
      289 +                op->op_oplock_state = OPLOCK_LEVEL_TWO;
      290 +                status = smb_oplock_request(sr, ofile,
      291 +                    &op->op_oplock_state);
      292 +        }
      293 +
      294 +        /*
      295 +         * Either of the above may have returned the
      296 +         * status code that says we should wait.
      297 +         */
      298 +        if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
      299 +                (void) smb2sr_go_async(sr);
      300 +                (void) smb_oplock_wait_break(ofile->f_node, 0);
      301 +                status = 0;
      302 +        }
      303 +
      304 +        /*
      305 +         * Keep track of what we got (in ofile->f_oplock.og_state)
      306 +         * so we'll know what we had when sending a break later.
      307 +         * The og_dialect here is the oplock dialect, not the
      308 +         * SMB dialect.  No lease here, so SMB 2.0.
      309 +         */
      310 +        ofile->f_oplock.og_dialect = SMB_VERS_2_002;
      311 +        switch (status) {
      312 +        case NT_STATUS_SUCCESS:
      313 +                ofile->f_oplock.og_state = op->op_oplock_state;
      314 +                break;
      315 +        case NT_STATUS_OPLOCK_NOT_GRANTED:
      316 +                ofile->f_oplock.og_state = 0;
      317 +                op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
      318 +                return;
      319 +        default:
      320 +                /* Caller did not check args sufficiently? */
      321 +                cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x",
      322 +                    sr->session->ip_addr_str, status);
      323 +                ofile->f_oplock.og_state = 0;
      324 +                op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
      325 +                return;
      326 +        }
      327 +
      328 +        /*
      329 +         * Have STATUS_SUCCESS
      330 +         * Convert internal oplock state to SMB2
      331 +         */
      332 +        if (op->op_oplock_state & OPLOCK_LEVEL_GRANULAR) {
      333 +                ASSERT(0);
      334 +                op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
      335 +        } else if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
      336 +                op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
      337 +        } else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
      338 +                op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
      339 +        } else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
      340 +                op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
      341 +        } else {
      342 +                op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
      343 +        }
 152  344  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX