1a90cf9f2SGordon Ross /* 2a90cf9f2SGordon Ross * This file and its contents are supplied under the terms of the 3a90cf9f2SGordon Ross * Common Development and Distribution License ("CDDL"), version 1.0. 4a90cf9f2SGordon Ross * You may only use this file in accordance with the terms of version 5a90cf9f2SGordon Ross * 1.0 of the CDDL. 6a90cf9f2SGordon Ross * 7a90cf9f2SGordon Ross * A full copy of the text of the CDDL should have accompanied this 8a90cf9f2SGordon Ross * source. A copy of the CDDL is also available via the Internet at 9a90cf9f2SGordon Ross * http://www.illumos.org/license/CDDL. 10a90cf9f2SGordon Ross */ 11a90cf9f2SGordon Ross 12a90cf9f2SGordon Ross /* 13*a8e9db1cSGordon Ross * Copyright 2020 Tintri by DDN, Inc. All rights reserved. 14a90cf9f2SGordon Ross */ 15a90cf9f2SGordon Ross 16a90cf9f2SGordon Ross /* 17a90cf9f2SGordon Ross * Dispatch function for SMB2_WRITE 18a90cf9f2SGordon Ross */ 19a90cf9f2SGordon Ross 20a90cf9f2SGordon Ross #include <smbsrv/smb2_kproto.h> 21a90cf9f2SGordon Ross #include <smbsrv/smb_fsops.h> 22a90cf9f2SGordon Ross 23dfa42fabSMatt Barden boolean_t smb_allow_unbuffered = B_TRUE; 24dfa42fabSMatt Barden 25a90cf9f2SGordon Ross smb_sdrc_t 26a90cf9f2SGordon Ross smb2_write(smb_request_t *sr) 27a90cf9f2SGordon Ross { 2893bc28dbSGordon Ross smb_rw_param_t *param = NULL; 29a90cf9f2SGordon Ross smb_ofile_t *of = NULL; 30a90cf9f2SGordon Ross smb_vdb_t *vdb = NULL; 31a90cf9f2SGordon Ross uint16_t StructSize; 32a90cf9f2SGordon Ross uint16_t DataOff; 33a90cf9f2SGordon Ross uint32_t Length; 34a90cf9f2SGordon Ross uint64_t Offset; 35a90cf9f2SGordon Ross smb2fid_t smb2fid; 36a90cf9f2SGordon Ross uint32_t Channel; 37a90cf9f2SGordon Ross uint32_t Remaining; 38a90cf9f2SGordon Ross uint16_t ChanInfoOffset; 39a90cf9f2SGordon Ross uint16_t ChanInfoLength; 40a90cf9f2SGordon Ross uint32_t Flags; 41a90cf9f2SGordon Ross uint32_t XferCount; 42a90cf9f2SGordon Ross uint32_t status; 43a90cf9f2SGordon Ross int data_chain_off, skip; 44a90cf9f2SGordon Ross int stability = 0; 45a90cf9f2SGordon Ross int rc = 0; 46dfa42fabSMatt Barden boolean_t unbuffered = B_FALSE; 47a90cf9f2SGordon Ross 48a90cf9f2SGordon Ross /* 4993bc28dbSGordon Ross * Decode SMB2 Write request 50a90cf9f2SGordon Ross */ 51a90cf9f2SGordon Ross rc = smb_mbc_decodef( 52a90cf9f2SGordon Ross &sr->smb_data, 53a90cf9f2SGordon Ross "wwlqqqllwwl", 54a90cf9f2SGordon Ross &StructSize, /* w */ 55a90cf9f2SGordon Ross &DataOff, /* w */ 56a90cf9f2SGordon Ross &Length, /* l */ 57a90cf9f2SGordon Ross &Offset, /* q */ 58a90cf9f2SGordon Ross &smb2fid.persistent, /* q */ 59a90cf9f2SGordon Ross &smb2fid.temporal, /* q */ 60a90cf9f2SGordon Ross &Channel, /* l */ 61a90cf9f2SGordon Ross &Remaining, /* l */ 62a90cf9f2SGordon Ross &ChanInfoOffset, /* w */ 63a90cf9f2SGordon Ross &ChanInfoLength, /* w */ 64a90cf9f2SGordon Ross &Flags); /* l */ 65a90cf9f2SGordon Ross if (rc) 66a90cf9f2SGordon Ross return (SDRC_ERROR); 67a90cf9f2SGordon Ross if (StructSize != 49) 68a90cf9f2SGordon Ross return (SDRC_ERROR); 69a90cf9f2SGordon Ross 7093bc28dbSGordon Ross /* 7193bc28dbSGordon Ross * Setup an smb_rw_param_t which contains the VDB we need. 7293bc28dbSGordon Ross * This is automatically free'd. 7393bc28dbSGordon Ross */ 7493bc28dbSGordon Ross param = smb_srm_zalloc(sr, sizeof (*param)); 7593bc28dbSGordon Ross param->rw_offset = Offset; 7693bc28dbSGordon Ross param->rw_count = Length; 7793bc28dbSGordon Ross /* Note that the dtrace provider uses sr->arg.rw */ 7893bc28dbSGordon Ross sr->arg.rw = param; 79a90cf9f2SGordon Ross 80a90cf9f2SGordon Ross /* 81a90cf9f2SGordon Ross * Skip any padding before the write data. 82a90cf9f2SGordon Ross */ 83a90cf9f2SGordon Ross data_chain_off = sr->smb2_cmd_hdr + DataOff; 84a90cf9f2SGordon Ross skip = data_chain_off - sr->smb_data.chain_offset; 8593bc28dbSGordon Ross if (skip < 0) 8693bc28dbSGordon Ross return (SDRC_ERROR); 8793bc28dbSGordon Ross if (skip > 0) 88a90cf9f2SGordon Ross (void) smb_mbc_decodef(&sr->smb_data, "#.", skip); 89a90cf9f2SGordon Ross 9093bc28dbSGordon Ross /* 9193bc28dbSGordon Ross * Decode the write data (payload) 9293bc28dbSGordon Ross */ 9393bc28dbSGordon Ross if (Length > smb2_max_rwsize) 9493bc28dbSGordon Ross return (SDRC_ERROR); 9593bc28dbSGordon Ross vdb = ¶m->rw_vdb; 96a90cf9f2SGordon Ross rc = smb_mbc_decodef(&sr->smb_data, "#B", Length, vdb); 9793bc28dbSGordon Ross if (rc != 0 || vdb->vdb_len != Length) 9893bc28dbSGordon Ross return (SDRC_ERROR); 99a90cf9f2SGordon Ross vdb->vdb_uio.uio_loffset = (offset_t)Offset; 100a90cf9f2SGordon Ross 10193bc28dbSGordon Ross /* 10293bc28dbSGordon Ross * Want FID lookup before the start probe. 10393bc28dbSGordon Ross */ 10493bc28dbSGordon Ross status = smb2sr_lookup_fid(sr, &smb2fid); 10593bc28dbSGordon Ross of = sr->fid_ofile; 10693bc28dbSGordon Ross 10793bc28dbSGordon Ross DTRACE_SMB2_START(op__Write, smb_request_t *, sr); /* arg.rw */ 10893bc28dbSGordon Ross 10993bc28dbSGordon Ross if (status) 11093bc28dbSGordon Ross goto errout; /* Bad FID */ 11193bc28dbSGordon Ross 11293bc28dbSGordon Ross 113a90cf9f2SGordon Ross XferCount = 0; 114a90cf9f2SGordon Ross if (Length == 0) 11593bc28dbSGordon Ross goto errout; 116a90cf9f2SGordon Ross 117dfa42fabSMatt Barden /* 118dfa42fabSMatt Barden * Unbuffered refers to the MS-FSA Write argument by the same name. 119dfa42fabSMatt Barden * It indicates that the cache for this range should be flushed to disk, 120dfa42fabSMatt Barden * and data written directly to disk, bypassing the cache. 121dfa42fabSMatt Barden * We don't allow that degree of cache management. 122dfa42fabSMatt Barden * Translate this directly as FSYNC, 123dfa42fabSMatt Barden * which should at least flush the cache. 124dfa42fabSMatt Barden */ 125dfa42fabSMatt Barden 126dfa42fabSMatt Barden if (smb_allow_unbuffered && 127dfa42fabSMatt Barden (Flags & SMB2_WRITEFLAG_WRITE_UNBUFFERED) != 0) 128dfa42fabSMatt Barden unbuffered = B_TRUE; 129dfa42fabSMatt Barden 130a90cf9f2SGordon Ross switch (of->f_tree->t_res_type & STYPE_MASK) { 131a90cf9f2SGordon Ross case STYPE_DISKTREE: 132a90cf9f2SGordon Ross case STYPE_PRINTQ: 133a90cf9f2SGordon Ross if (!smb_node_is_dir(of->f_node)) { 134a90cf9f2SGordon Ross /* Check for conflicting locks. */ 135a90cf9f2SGordon Ross rc = smb_lock_range_access(sr, of->f_node, 136a90cf9f2SGordon Ross Offset, Length, B_TRUE); 137a90cf9f2SGordon Ross if (rc) { 138a90cf9f2SGordon Ross rc = ERANGE; 139a90cf9f2SGordon Ross break; 140a90cf9f2SGordon Ross } 141a90cf9f2SGordon Ross } 142dfa42fabSMatt Barden 143dfa42fabSMatt Barden if (unbuffered || (Flags & SMB2_WRITEFLAG_WRITE_THROUGH) != 0 || 144dfa42fabSMatt Barden (of->f_node->flags & NODE_FLAGS_WRITE_THROUGH) != 0) { 145a90cf9f2SGordon Ross stability = FSYNC; 146a90cf9f2SGordon Ross } 14755f0a249SGordon Ross rc = smb_fsop_write(sr, of->f_cr, of->f_node, of, 148a90cf9f2SGordon Ross &vdb->vdb_uio, &XferCount, stability); 149a90cf9f2SGordon Ross if (rc) 150a90cf9f2SGordon Ross break; 15194047d49SGordon Ross /* This revokes read cache delegations. */ 15294047d49SGordon Ross (void) smb_oplock_break_WRITE(of->f_node, of); 153a90cf9f2SGordon Ross break; 154a90cf9f2SGordon Ross 155a90cf9f2SGordon Ross case STYPE_IPC: 156dfa42fabSMatt Barden if (unbuffered || (Flags & SMB2_WRITEFLAG_WRITE_THROUGH) != 0) 157dfa42fabSMatt Barden rc = EINVAL; 158dfa42fabSMatt Barden else 159a90cf9f2SGordon Ross rc = smb_opipe_write(sr, &vdb->vdb_uio); 160a90cf9f2SGordon Ross if (rc == 0) 161a90cf9f2SGordon Ross XferCount = Length; 162a90cf9f2SGordon Ross break; 163a90cf9f2SGordon Ross 164a90cf9f2SGordon Ross default: 165a90cf9f2SGordon Ross rc = EACCES; 166a90cf9f2SGordon Ross break; 167a90cf9f2SGordon Ross } 16893bc28dbSGordon Ross status = smb_errno2status(rc); 169a90cf9f2SGordon Ross 17093bc28dbSGordon Ross errout: 17193bc28dbSGordon Ross sr->smb2_status = status; 17293bc28dbSGordon Ross DTRACE_SMB2_DONE(op__Write, smb_request_t *, sr); /* arg.rw */ 17393bc28dbSGordon Ross 17493bc28dbSGordon Ross if (status) { 17593bc28dbSGordon Ross smb2sr_put_error(sr, status); 176a90cf9f2SGordon Ross return (SDRC_SUCCESS); 177a90cf9f2SGordon Ross } 178a90cf9f2SGordon Ross 179a90cf9f2SGordon Ross /* 18093bc28dbSGordon Ross * Encode SMB2 Write reply 181a90cf9f2SGordon Ross */ 182a90cf9f2SGordon Ross DataOff = SMB2_HDR_SIZE + 16; 183a90cf9f2SGordon Ross rc = smb_mbc_encodef( 184a90cf9f2SGordon Ross &sr->reply, "wwlll", 185a90cf9f2SGordon Ross 17, /* StructSize */ /* w */ 186a90cf9f2SGordon Ross 0, /* reserved */ /* w */ 187a90cf9f2SGordon Ross XferCount, /* l */ 188a90cf9f2SGordon Ross 0, /* DataRemaining */ /* l */ 189a90cf9f2SGordon Ross 0); /* Channel Info */ /* l */ 19093bc28dbSGordon Ross if (rc) { 19193bc28dbSGordon Ross sr->smb2_status = NT_STATUS_INTERNAL_ERROR; 192a90cf9f2SGordon Ross return (SDRC_ERROR); 19393bc28dbSGordon Ross } 194a90cf9f2SGordon Ross 195a90cf9f2SGordon Ross mutex_enter(&of->f_mutex); 196a90cf9f2SGordon Ross of->f_seek_pos = Offset + XferCount; 197a90cf9f2SGordon Ross mutex_exit(&of->f_mutex); 198a90cf9f2SGordon Ross 199a90cf9f2SGordon Ross return (SDRC_SUCCESS); 200a90cf9f2SGordon Ross } 201