1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 /*
  27  * smb1 oplock support
  28  */
  29 
  30 #include <smbsrv/smb_kproto.h>
  31 
  32 #define BATCH_OR_EXCL   (OPLOCK_LEVEL_BATCH | OPLOCK_LEVEL_ONE)
  33 
  34 /*
  35  * Client has an open handle and requests an oplock.
  36  * Convert SMB1 oplock request info in to internal form,
  37  * call common oplock code, convert result to SMB1.
  38  */
  39 void
  40 smb1_oplock_acquire(smb_request_t *sr, boolean_t level2ok)
  41 {
  42         smb_arg_open_t *op = &sr->arg.open;
  43         smb_ofile_t *ofile = sr->fid_ofile;
  44         uint32_t status;
  45 
  46         /* Only disk trees get oplocks. */
  47         if ((sr->tid_tree->t_res_type & STYPE_MASK) != STYPE_DISKTREE) {
  48                 op->op_oplock_level = SMB_OPLOCK_NONE;
  49                 return;
  50         }
  51 
  52         if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) {
  53                 op->op_oplock_level = SMB_OPLOCK_NONE;
  54                 return;
  55         }
  56 
  57         if (!smb_session_levelII_oplocks(sr->session))
  58                 level2ok = B_FALSE;
  59 
  60         /* Common code checks file type. */
  61 
  62         /*
  63          * SMB1: Convert to internal form.
  64          */
  65         switch (op->op_oplock_level) {
  66         case SMB_OPLOCK_BATCH:
  67                 op->op_oplock_state = OPLOCK_LEVEL_BATCH;
  68                 break;
  69         case SMB_OPLOCK_EXCLUSIVE:
  70                 op->op_oplock_state = OPLOCK_LEVEL_ONE;
  71                 break;
  72         case SMB_OPLOCK_LEVEL_II:
  73                 op->op_oplock_state = OPLOCK_LEVEL_TWO;
  74                 break;
  75         case SMB_OPLOCK_NONE:
  76         default:
  77                 op->op_oplock_level = SMB_OPLOCK_NONE;
  78                 return;
  79         }
  80 
  81         /*
  82          * Tree options may force shared oplocks
  83          */
  84         if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) {
  85                 op->op_oplock_state = OPLOCK_LEVEL_TWO;
  86         }
  87 
  88         /*
  89          * Try exclusive first, if requested
  90          */
  91         if ((op->op_oplock_state & BATCH_OR_EXCL) != 0) {
  92                 status = smb_oplock_request(sr, ofile,
  93                     &op->op_oplock_state);
  94         } else {
  95                 status = NT_STATUS_OPLOCK_NOT_GRANTED;
  96         }
  97 
  98         /*
  99          * If exclusive failed (or tree forced shared oplocks)
 100          * and if the caller supports Level II, try shared.
 101          */
 102         if (status == NT_STATUS_OPLOCK_NOT_GRANTED && level2ok) {
 103                 op->op_oplock_state = OPLOCK_LEVEL_TWO;
 104                 status = smb_oplock_request(sr, ofile,
 105                     &op->op_oplock_state);
 106         }
 107 
 108         /*
 109          * Either of the above may have returned the
 110          * status code that says we should wait.
 111          */
 112         if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
 113                 (void) smb_oplock_wait_break(ofile->f_node, 0);
 114                 status = 0;
 115         }
 116 
 117         /*
 118          * Keep track of what we got (in ofile->f_oplock.og_state)
 119          * so we'll know what we had when sending a break later.
 120          * The og_dialect here is the oplock dialect, which may be
 121          * different than SMB dialect.  Pre-NT clients did not
 122          * support "Level II" oplocks.  If we're talking to a
 123          * client that didn't set the CAP_LEVEL_II_OPLOCKS in
 124          * its capabilities, let og_dialect = LANMAN2_1.
 125          */
 126         ofile->f_oplock.og_dialect = (level2ok) ?
 127             NT_LM_0_12 : LANMAN2_1;
 128         switch (status) {
 129         case NT_STATUS_SUCCESS:
 130                 ofile->f_oplock.og_state = op->op_oplock_state;
 131                 break;
 132         case NT_STATUS_OPLOCK_NOT_GRANTED:
 133                 ofile->f_oplock.og_state = 0;
 134                 op->op_oplock_level = SMB_OPLOCK_NONE;
 135                 return;
 136         default:
 137                 /* Caller did not check args sufficiently? */
 138                 cmn_err(CE_NOTE, "clnt %s oplock req. err 0x%x",
 139                     sr->session->ip_addr_str, status);
 140                 ofile->f_oplock.og_state = 0;
 141                 op->op_oplock_level = SMB_OPLOCK_NONE;
 142                 return;
 143         }
 144 
 145         /*
 146          * Have STATUS_SUCCESS
 147          * Convert internal oplock state to SMB1
 148          */
 149         if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
 150                 op->op_oplock_level = SMB_OPLOCK_BATCH;
 151         } else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
 152                 op->op_oplock_level = SMB_OPLOCK_EXCLUSIVE;
 153         } else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
 154                 op->op_oplock_level = SMB_OPLOCK_LEVEL_II;
 155         } else {
 156                 op->op_oplock_level = SMB_OPLOCK_NONE;
 157         }
 158 }