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 2014 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * Dispatch function for SMB2_LOCK
  18  */
  19 
  20 #include <smbsrv/smb2_kproto.h>
  21 
  22 struct SMB2_LOCK_ELEMENT {
  23         uint64_t Offset;
  24         uint64_t Length;
  25         uint32_t Flags;
  26         uint32_t reserved;
  27 };
  28 
  29 static smb_sdrc_t smb2_lock_async(smb_request_t *);
  30 static uint32_t smb2_lock_exec(smb_request_t *, uint16_t);
  31 static uint32_t smb2_lock_elem(smb_request_t *, struct SMB2_LOCK_ELEMENT *);
  32 
  33 /*
  34  * This is a somewhat arbitrary sanity limit on the length of the
  35  * SMB2_LOCK_ELEMENT array.  It usually has length one or two.
  36  */
  37 int smb2_lock_max_elem = 1024;
  38 
  39 smb_sdrc_t
  40 smb2_lock(smb_request_t *sr)
  41 {
  42         struct SMB2_LOCK_ELEMENT elem;
  43         smb2fid_t smb2fid;
  44         uint32_t save_offset;
  45         uint32_t LockSequence;
  46         uint32_t status;
  47         uint16_t StructSize;
  48         uint16_t LockCount;
  49         uint16_t i;
  50         boolean_t MayBlock = B_FALSE;
  51         int rc = 0;
  52 
  53         /*
  54          * SMB2 Lock request
  55          */
  56         rc = smb_mbc_decodef(
  57             &sr->smb_data, "wwlqq",
  58             &StructSize,            /* w */
  59             &LockCount,                     /* w */
  60             &LockSequence,          /* l */
  61             &smb2fid.persistent,    /* q */
  62             &smb2fid.temporal);             /* q */
  63         if (rc || StructSize != 48)
  64                 return (SDRC_ERROR);
  65 
  66         status = smb2sr_lookup_fid(sr, &smb2fid);
  67         if (status)
  68                 goto errout;
  69         if (sr->fid_ofile->f_node == NULL || LockCount == 0) {
  70                 status = NT_STATUS_INVALID_PARAMETER;
  71                 goto errout;
  72         }
  73         if (LockCount > smb2_lock_max_elem) {
  74                 status = NT_STATUS_INSUFFICIENT_RESOURCES;
  75                 goto errout;
  76         }
  77 
  78         /*
  79          * Process the array of SMB2_LOCK_ELEMENT structs
  80          * We do this twice.  (it's always a short list)
  81          * The first time, just validate the flags, and check
  82          * if any of the locking request might need to block.
  83          * The second time (either here, or in the async
  84          * handler function) process the locks for real.
  85          */
  86         save_offset = sr->smb_data.chain_offset;
  87         for (i = 0; i < LockCount; i++) {
  88                 rc = smb_mbc_decodef(
  89                     &sr->smb_data, "qqll",
  90                     &elem.Offset,   /* q */
  91                     &elem.Length,   /* q */
  92                     &elem.Flags,    /* l */
  93                     &elem.reserved);        /* l */
  94                 if (rc) {
  95                         status = NT_STATUS_INVALID_PARAMETER;
  96                         goto errout;
  97                 }
  98 
  99                 /*
 100                  * Make sure the flags are valid;
 101                  * Find out if we might block.
 102                  */
 103                 switch (elem.Flags) {
 104                 case SMB2_LOCKFLAG_SHARED_LOCK:
 105                 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
 106                         MayBlock = B_TRUE;
 107                         break;
 108 
 109                 /* BEGIN CSTYLED */
 110                 case SMB2_LOCKFLAG_SHARED_LOCK |
 111                      SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
 112                 case SMB2_LOCKFLAG_EXCLUSIVE_LOCK |
 113                      SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
 114                 case SMB2_LOCKFLAG_UNLOCK:
 115                 /* END CSTYLED */
 116                         break;
 117 
 118                 default:
 119                         status = NT_STATUS_INVALID_PARAMETER;
 120                         goto errout;
 121                 }
 122         }
 123 
 124         if (MayBlock) {
 125                 /*
 126                  * May need to block.  "Go async".
 127                  */
 128                 status = smb2sr_go_async(sr, smb2_lock_async);
 129                 goto errout;
 130         }
 131 
 132         sr->smb_data.chain_offset = save_offset;
 133         status = smb2_lock_exec(sr, LockCount);
 134         if (status)
 135                 goto errout;
 136 
 137         /*
 138          * SMB2 Lock reply (sync)
 139          */
 140         StructSize = 4;
 141         (void) smb_mbc_encodef(
 142             &sr->reply, "w..",
 143             StructSize);        /* w */
 144             /* reserved           .. */
 145         return (SDRC_SUCCESS);
 146 
 147 errout:
 148         smb2sr_put_error(sr, status);
 149         return (SDRC_SUCCESS);
 150 }
 151 
 152 static smb_sdrc_t
 153 smb2_lock_async(smb_request_t *sr)
 154 {
 155         smb2fid_t smb2fid;
 156         uint32_t LockSequence;
 157         uint32_t status;
 158         uint16_t StructSize;
 159         uint16_t LockCount;
 160         int rc = 0;
 161 
 162         /*
 163          * Decode the lock request again.  It should all decode
 164          * exactly the same as the first time we saw it.  If not,
 165          * report an "internal error".
 166          */
 167         rc = smb_mbc_decodef(
 168             &sr->smb_data, "wwlqq",
 169             &StructSize,            /* w */
 170             &LockCount,                     /* w */
 171             &LockSequence,          /* l */
 172             &smb2fid.persistent,    /* q */
 173             &smb2fid.temporal);             /* q */
 174         if (rc || StructSize != 48)
 175                 return (SDRC_ERROR);
 176 
 177         status = smb2sr_lookup_fid(sr, &smb2fid);
 178         if (status)
 179                 goto errout;
 180         if (sr->fid_ofile->f_node == NULL || LockCount == 0) {
 181                 status = NT_STATUS_INTERNAL_ERROR;
 182                 goto errout;
 183         }
 184 
 185         status = smb2_lock_exec(sr, LockCount);
 186         if (status)
 187                 goto errout;
 188 
 189         /*
 190          * SMB2 Lock reply (async)
 191          */
 192         StructSize = 4;
 193         (void) smb_mbc_encodef(
 194             &sr->reply, "w..",
 195             StructSize);        /* w */
 196             /* reserved           .. */
 197         return (SDRC_SUCCESS);
 198 
 199 errout:
 200         smb2sr_put_error(sr, status);
 201         return (SDRC_SUCCESS);
 202 }
 203 
 204 /*
 205  * Execute the vector of locks.  This is the common function called by
 206  * either the sync or async code paths.  We've already decoded this
 207  * request once when we get here, so if there are any decode errors
 208  * then it's some kind of internal error.
 209  */
 210 static uint32_t
 211 smb2_lock_exec(smb_request_t *sr, uint16_t LockCount)
 212 {
 213         struct SMB2_LOCK_ELEMENT elem;
 214         uint32_t status = 0;
 215         uint16_t i;
 216         int rc;
 217 
 218         /*
 219          * On entry, out position in the input data should be
 220          * after both the SMB2 header and the fixed part of
 221          * the SMB Lock request header (24).
 222          */
 223         ASSERT(sr->smb_data.chain_offset ==
 224             (sr->smb2_cmd_hdr + SMB2_HDR_SIZE + 24));
 225 
 226         /*
 227          * This is checked by our callers, but let's make sure.
 228          */
 229         ASSERT(sr->fid_ofile->f_node != NULL);
 230 
 231         for (i = 0; i < LockCount; i++) {
 232                 rc = smb_mbc_decodef(
 233                     &sr->smb_data, "qqll",
 234                     &elem.Offset,   /* q */
 235                     &elem.Length,   /* q */
 236                     &elem.Flags,    /* l */
 237                     &elem.reserved);        /* l */
 238                 if (rc) {
 239                         status = NT_STATUS_INTERNAL_ERROR;
 240                         break;
 241                 }
 242                 status = smb2_lock_elem(sr, &elem);
 243                 if (status)
 244                         break;
 245         }
 246         return (status);
 247 }
 248 
 249 static uint32_t
 250 smb2_lock_elem(smb_request_t *sr, struct SMB2_LOCK_ELEMENT *elem)
 251 {
 252         smb_node_t *node = sr->fid_ofile->f_node;
 253         uint32_t status;
 254         uint32_t ltype;
 255         uint32_t timeout = 0;
 256 
 257         switch (elem->Flags) {
 258         case SMB2_LOCKFLAG_SHARED_LOCK:
 259                 timeout = UINT_MAX;
 260                 /* FALLTHROUGH */
 261         case SMB2_LOCKFLAG_SHARED_LOCK | SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
 262                 ltype = SMB_LOCK_TYPE_READONLY;
 263                 status = smb_lock_range(sr,
 264                     elem->Offset, elem->Length,
 265                     timeout, ltype);
 266                 break;
 267 
 268         case SMB2_LOCKFLAG_EXCLUSIVE_LOCK:
 269                 timeout = UINT_MAX;
 270                 /* FALLTHROUGH */
 271         case SMB2_LOCKFLAG_EXCLUSIVE_LOCK | SMB2_LOCKFLAG_FAIL_IMMEDIATELY:
 272                 ltype = SMB_LOCK_TYPE_READWRITE;
 273                 status = smb_lock_range(sr,
 274                     elem->Offset, elem->Length,
 275                     timeout, ltype);
 276                 break;
 277 
 278         case SMB2_LOCKFLAG_UNLOCK:
 279                 status = smb_unlock_range(sr, node,
 280                     elem->Offset, elem->Length);
 281                 break;
 282 
 283         /*
 284          * We've already checked the flags previously, so any
 285          * surprises here are some kind of internal error.
 286          */
 287         default:
 288                 status = NT_STATUS_INTERNAL_ERROR;
 289                 break;
 290         }
 291 
 292         return (status);
 293 }