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_WRITE
  18  */
  19 
  20 #include <smbsrv/smb2_kproto.h>
  21 #include <smbsrv/smb_fsops.h>
  22 
  23 smb_sdrc_t
  24 smb2_write(smb_request_t *sr)
  25 {
  26         smb_ofile_t *of = NULL;
  27         smb_vdb_t *vdb = NULL;
  28         uint16_t StructSize;
  29         uint16_t DataOff;
  30         uint32_t Length;
  31         uint64_t Offset;
  32         smb2fid_t smb2fid;
  33         uint32_t Channel;
  34         uint32_t Remaining;
  35         uint16_t ChanInfoOffset;
  36         uint16_t ChanInfoLength;
  37         uint32_t Flags;
  38         uint32_t XferCount;
  39         uint32_t status;
  40         int data_chain_off, skip;
  41         int stability = 0;
  42         int rc = 0;
  43 
  44         /*
  45          * SMB2 Write request
  46          */
  47         rc = smb_mbc_decodef(
  48             &sr->smb_data,
  49             "wwlqqqllwwl",
  50             &StructSize,            /* w */
  51             &DataOff,                       /* w */
  52             &Length,                        /* l */
  53             &Offset,                        /* q */
  54             &smb2fid.persistent,    /* q */
  55             &smb2fid.temporal,              /* q */
  56             &Channel,                       /* l */
  57             &Remaining,                     /* l */
  58             &ChanInfoOffset,                /* w */
  59             &ChanInfoLength,                /* w */
  60             &Flags);                        /* l */
  61         if (rc)
  62                 return (SDRC_ERROR);
  63         if (StructSize != 49)
  64                 return (SDRC_ERROR);
  65 
  66         status = smb2sr_lookup_fid(sr, &smb2fid);
  67         if (status) {
  68                 smb2sr_put_error(sr, status);
  69                 return (SDRC_SUCCESS);
  70         }
  71         of = sr->fid_ofile;
  72 
  73         if (Length > smb2_max_rwsize) {
  74                 smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
  75                 return (SDRC_SUCCESS);
  76         }
  77 
  78         /*
  79          * Skip any padding before the write data.
  80          */
  81         data_chain_off = sr->smb2_cmd_hdr + DataOff;
  82         skip = data_chain_off - sr->smb_data.chain_offset;
  83         if (skip < 0) {
  84                 smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
  85                 return (SDRC_SUCCESS);
  86         }
  87         if (skip > 0) {
  88                 (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
  89         }
  90 
  91         /* This is automatically free'd. */
  92         vdb = smb_srm_zalloc(sr, sizeof (*vdb));
  93         rc = smb_mbc_decodef(&sr->smb_data, "#B", Length, vdb);
  94         if (rc != 0 || vdb->vdb_len != Length) {
  95                 smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
  96                 return (SDRC_SUCCESS);
  97         }
  98         vdb->vdb_uio.uio_loffset = (offset_t)Offset;
  99 
 100         XferCount = 0;
 101         if (Length == 0)
 102                 goto doreply;
 103 
 104         switch (of->f_tree->t_res_type & STYPE_MASK) {
 105         case STYPE_DISKTREE:
 106         case STYPE_PRINTQ:
 107                 if (!smb_node_is_dir(of->f_node)) {
 108                         /* Check for conflicting locks. */
 109                         rc = smb_lock_range_access(sr, of->f_node,
 110                             Offset, Length, B_TRUE);
 111                         if (rc) {
 112                                 rc = ERANGE;
 113                                 break;
 114                         }
 115                 }
 116                 if ((Flags & SMB2_WRITEFLAG_WRITE_THROUGH) ||
 117                     (of->f_node->flags & NODE_FLAGS_WRITE_THROUGH)) {
 118                         stability = FSYNC;
 119                 }
 120                 rc = smb_fsop_write(sr, of->f_cr, of->f_node,
 121                     &vdb->vdb_uio, &XferCount, stability);
 122                 if (rc)
 123                         break;
 124                 of->f_written = B_TRUE;
 125                 if (!smb_node_is_dir(of->f_node))
 126                         smb_oplock_break_levelII(of->f_node);
 127                 break;
 128 
 129         case STYPE_IPC:
 130                 rc = smb_opipe_write(sr, &vdb->vdb_uio);
 131                 if (rc == 0)
 132                         XferCount = Length;
 133                 break;
 134 
 135         default:
 136                 rc = EACCES;
 137                 break;
 138         }
 139 
 140         if (rc) {
 141                 smb2sr_put_errno(sr, rc);
 142                 return (SDRC_SUCCESS);
 143         }
 144 
 145         /*
 146          * SMB2 Write reply
 147          */
 148 doreply:
 149         DataOff = SMB2_HDR_SIZE + 16;
 150         rc = smb_mbc_encodef(
 151             &sr->reply, "wwlll",
 152             17, /* StructSize */        /* w */
 153             0, /* reserved */           /* w */
 154             XferCount,                  /* l */
 155             0, /* DataRemaining */      /* l */
 156             0); /* Channel Info */      /* l */
 157         if (rc)
 158                 return (SDRC_ERROR);
 159 
 160         mutex_enter(&of->f_mutex);
 161         of->f_seek_pos = Offset + XferCount;
 162         mutex_exit(&of->f_mutex);
 163 
 164         return (SDRC_SUCCESS);
 165 }