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 }
 | 
   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  */
  15 
  16 /*
  17  * Dispatch function for SMB2_WRITE
  18  */
  19 
  20 #include <smbsrv/smb2_kproto.h>
  21 #include <smbsrv/smb_fsops.h>
  22 
  23 boolean_t smb_allow_unbuffered = B_TRUE;
  24 
  25 smb_sdrc_t
  26 smb2_write(smb_request_t *sr)
  27 {
  28         smb_ofile_t *of = NULL;
  29         smb_vdb_t *vdb = NULL;
  30         uint16_t StructSize;
  31         uint16_t DataOff;
  32         uint32_t Length;
  33         uint64_t Offset;
  34         smb2fid_t smb2fid;
  35         uint32_t Channel;
  36         uint32_t Remaining;
  37         uint16_t ChanInfoOffset;
  38         uint16_t ChanInfoLength;
  39         uint32_t Flags;
  40         uint32_t XferCount;
  41         uint32_t status;
  42         int data_chain_off, skip;
  43         int stability = 0;
  44         int rc = 0;
  45         boolean_t unbuffered = B_FALSE;
  46 
  47         /*
  48          * Decode SMB2 Write request
  49          */
  50         rc = smb_mbc_decodef(
  51             &sr->smb_data,
  52             "wwlqqqllwwl",
  53             &StructSize,            /* w */
  54             &DataOff,                       /* w */
  55             &Length,                        /* l */
  56             &Offset,                        /* q */
  57             &smb2fid.persistent,    /* q */
  58             &smb2fid.temporal,              /* q */
  59             &Channel,                       /* l */
  60             &Remaining,                     /* l */
  61             &ChanInfoOffset,                /* w */
  62             &ChanInfoLength,                /* w */
  63             &Flags);                        /* l */
  64         if (rc)
  65                 return (SDRC_ERROR);
  66         if (StructSize != 49)
  67                 return (SDRC_ERROR);
  68 
  69         /*
  70          * Skip any padding before the write data.
  71          */
  72         data_chain_off = sr->smb2_cmd_hdr + DataOff;
  73         skip = data_chain_off - sr->smb_data.chain_offset;
  74         if (skip < 0)
  75                 return (SDRC_ERROR);
  76         if (skip > 0)
  77                 (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
  78 
  79         /*
  80          * Decode the write data (payload)
  81          * This is automatically free'd.
  82          */
  83         if (Length > smb2_max_rwsize)
  84                 return (SDRC_ERROR);
  85         vdb = smb_srm_zalloc(sr, sizeof (*vdb));
  86         rc = smb_mbc_decodef(&sr->smb_data, "#B", Length, vdb);
  87         if (rc != 0 || vdb->vdb_len != Length)
  88                 return (SDRC_ERROR);
  89         vdb->vdb_uio.uio_loffset = (offset_t)Offset;
  90 
  91         /*
  92          * Want FID lookup before the start probe.
  93          */
  94         status = smb2sr_lookup_fid(sr, &smb2fid);
  95         of = sr->fid_ofile;
  96 
  97         DTRACE_SMB2_START(op__Write, smb_request_t *, sr); /* arg.rw */
  98 
  99         if (status)
 100                 goto errout; /* Bad FID */
 101 
 102 
 103         XferCount = 0;
 104         if (Length == 0)
 105                 goto errout;
 106 
 107         /*
 108          * Unbuffered refers to the MS-FSA Write argument by the same name.
 109          * It indicates that the cache for this range should be flushed to disk,
 110          * and data written directly to disk, bypassing the cache.
 111          * We don't allow that degree of cache management.
 112          * Translate this directly as FSYNC,
 113          * which should at least flush the cache.
 114          */
 115 
 116         if (smb_allow_unbuffered &&
 117             (Flags & SMB2_WRITEFLAG_WRITE_UNBUFFERED) != 0)
 118                 unbuffered = B_TRUE;
 119 
 120         switch (of->f_tree->t_res_type & STYPE_MASK) {
 121         case STYPE_DISKTREE:
 122         case STYPE_PRINTQ:
 123                 if (!smb_node_is_dir(of->f_node)) {
 124                         /* Check for conflicting locks. */
 125                         rc = smb_lock_range_access(sr, of->f_node,
 126                             Offset, Length, B_TRUE);
 127                         if (rc) {
 128                                 rc = ERANGE;
 129                                 break;
 130                         }
 131                 }
 132 
 133                 if (unbuffered || (Flags & SMB2_WRITEFLAG_WRITE_THROUGH) != 0 ||
 134                     (of->f_node->flags & NODE_FLAGS_WRITE_THROUGH) != 0) {
 135                         stability = FSYNC;
 136                 }
 137                 rc = smb_fsop_write(sr, of->f_cr, of->f_node, of,
 138                     &vdb->vdb_uio, &XferCount, stability);
 139                 if (rc)
 140                         break;
 141                 of->f_written = B_TRUE;
 142                 /* This revokes read cache delegations. */
 143                 (void) smb_oplock_break_WRITE(of->f_node, of);
 144                 break;
 145 
 146         case STYPE_IPC:
 147                 if (unbuffered || (Flags & SMB2_WRITEFLAG_WRITE_THROUGH) != 0)
 148                         rc = EINVAL;
 149                 else
 150                         rc = smb_opipe_write(sr, &vdb->vdb_uio);
 151                 if (rc == 0)
 152                         XferCount = Length;
 153                 break;
 154 
 155         default:
 156                 rc = EACCES;
 157                 break;
 158         }
 159         status = smb_errno2status(rc);
 160 
 161 errout:
 162         sr->smb2_status = status;
 163         DTRACE_SMB2_DONE(op__Write, smb_request_t *, sr); /* arg.rw */
 164 
 165         if (status) {
 166                 smb2sr_put_error(sr, status);
 167                 return (SDRC_SUCCESS);
 168         }
 169 
 170         /*
 171          * Encode SMB2 Write reply
 172          */
 173         DataOff = SMB2_HDR_SIZE + 16;
 174         rc = smb_mbc_encodef(
 175             &sr->reply, "wwlll",
 176             17, /* StructSize */        /* w */
 177             0, /* reserved */           /* w */
 178             XferCount,                  /* l */
 179             0, /* DataRemaining */      /* l */
 180             0); /* Channel Info */      /* l */
 181         if (rc) {
 182                 sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
 183                 return (SDRC_ERROR);
 184         }
 185 
 186         mutex_enter(&of->f_mutex);
 187         of->f_seek_pos = Offset + XferCount;
 188         mutex_exit(&of->f_mutex);
 189 
 190         return (SDRC_SUCCESS);
 191 }
 |