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