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 2019 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_rw_param_t *param = NULL; 27 smb_ofile_t *of = NULL; 28 smb_vdb_t *vdb = NULL; 29 uint16_t StructSize; 30 uint16_t DataOff; 31 uint32_t Length; 32 uint64_t Offset; 33 smb2fid_t smb2fid; 34 uint32_t Channel; 35 uint32_t Remaining; 36 uint16_t ChanInfoOffset; 37 uint16_t ChanInfoLength; 38 uint32_t Flags; 39 uint32_t XferCount; 40 uint32_t status; 41 int data_chain_off, skip; 42 int stability = 0; 43 int rc = 0; 44 45 /* 46 * Decode SMB2 Write request 47 */ 48 rc = smb_mbc_decodef( 49 &sr->smb_data, 50 "wwlqqqllwwl", 51 &StructSize, /* w */ 52 &DataOff, /* w */ 53 &Length, /* l */ 54 &Offset, /* q */ 55 &smb2fid.persistent, /* q */ 56 &smb2fid.temporal, /* q */ 57 &Channel, /* l */ 58 &Remaining, /* l */ 59 &ChanInfoOffset, /* w */ 60 &ChanInfoLength, /* w */ 61 &Flags); /* l */ 62 if (rc) 63 return (SDRC_ERROR); 64 if (StructSize != 49) 65 return (SDRC_ERROR); 66 67 /* 68 * Setup an smb_rw_param_t which contains the VDB we need. 69 * This is automatically free'd. 70 */ 71 param = smb_srm_zalloc(sr, sizeof (*param)); 72 param->rw_offset = Offset; 73 param->rw_count = Length; 74 /* Note that the dtrace provider uses sr->arg.rw */ 75 sr->arg.rw = param; 76 77 /* 78 * Skip any padding before the write data. 79 */ 80 data_chain_off = sr->smb2_cmd_hdr + DataOff; 81 skip = data_chain_off - sr->smb_data.chain_offset; 82 if (skip < 0) 83 return (SDRC_ERROR); 84 if (skip > 0) 85 (void) smb_mbc_decodef(&sr->smb_data, "#.", skip); 86 87 /* 88 * Decode the write data (payload) 89 */ 90 if (Length > smb2_max_rwsize) 91 return (SDRC_ERROR); 92 vdb = ¶m->rw_vdb; 93 rc = smb_mbc_decodef(&sr->smb_data, "#B", Length, vdb); 94 if (rc != 0 || vdb->vdb_len != Length) 95 return (SDRC_ERROR); 96 vdb->vdb_uio.uio_loffset = (offset_t)Offset; 97 98 /* 99 * Want FID lookup before the start probe. 100 */ 101 status = smb2sr_lookup_fid(sr, &smb2fid); 102 of = sr->fid_ofile; 103 104 DTRACE_SMB2_START(op__Write, smb_request_t *, sr); /* arg.rw */ 105 106 if (status) 107 goto errout; /* Bad FID */ 108 109 110 XferCount = 0; 111 if (Length == 0) 112 goto errout; 113 114 switch (of->f_tree->t_res_type & STYPE_MASK) { 115 case STYPE_DISKTREE: 116 case STYPE_PRINTQ: 117 if (!smb_node_is_dir(of->f_node)) { 118 /* Check for conflicting locks. */ 119 rc = smb_lock_range_access(sr, of->f_node, 120 Offset, Length, B_TRUE); 121 if (rc) { 122 rc = ERANGE; 123 break; 124 } 125 } 126 if ((Flags & SMB2_WRITEFLAG_WRITE_THROUGH) || 127 (of->f_node->flags & NODE_FLAGS_WRITE_THROUGH)) { 128 stability = FSYNC; 129 } 130 rc = smb_fsop_write(sr, of->f_cr, of->f_node, 131 &vdb->vdb_uio, &XferCount, stability); 132 if (rc) 133 break; 134 of->f_written = B_TRUE; 135 if (!smb_node_is_dir(of->f_node)) 136 smb_oplock_break_levelII(of->f_node); 137 break; 138 139 case STYPE_IPC: 140 rc = smb_opipe_write(sr, &vdb->vdb_uio); 141 if (rc == 0) 142 XferCount = Length; 143 break; 144 145 default: 146 rc = EACCES; 147 break; 148 } 149 status = smb_errno2status(rc); 150 151 errout: 152 sr->smb2_status = status; 153 DTRACE_SMB2_DONE(op__Write, smb_request_t *, sr); /* arg.rw */ 154 155 if (status) { 156 smb2sr_put_error(sr, status); 157 return (SDRC_SUCCESS); 158 } 159 160 /* 161 * Encode SMB2 Write reply 162 */ 163 DataOff = SMB2_HDR_SIZE + 16; 164 rc = smb_mbc_encodef( 165 &sr->reply, "wwlll", 166 17, /* StructSize */ /* w */ 167 0, /* reserved */ /* w */ 168 XferCount, /* l */ 169 0, /* DataRemaining */ /* l */ 170 0); /* Channel Info */ /* l */ 171 if (rc) { 172 sr->smb2_status = NT_STATUS_INTERNAL_ERROR; 173 return (SDRC_ERROR); 174 } 175 176 mutex_enter(&of->f_mutex); 177 of->f_seek_pos = Offset + XferCount; 178 mutex_exit(&of->f_mutex); 179 180 return (SDRC_SUCCESS); 181 } 182