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