xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_write.c (revision f8e30ca2ec5c9a7f5bd81df127b915fdc6bb0c1a)
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 /*
13a8e9db1cSGordon Ross  * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
14*f8e30ca2SGordon Ross  * Copyright 2022 RackTop Systems, Inc.
15a90cf9f2SGordon Ross  */
16a90cf9f2SGordon Ross 
17a90cf9f2SGordon Ross /*
18a90cf9f2SGordon Ross  * Dispatch function for SMB2_WRITE
19a90cf9f2SGordon Ross  */
20a90cf9f2SGordon Ross 
21a90cf9f2SGordon Ross #include <smbsrv/smb2_kproto.h>
22a90cf9f2SGordon Ross #include <smbsrv/smb_fsops.h>
23a90cf9f2SGordon Ross 
24dfa42fabSMatt Barden boolean_t smb_allow_unbuffered = B_TRUE;
25dfa42fabSMatt Barden 
26a90cf9f2SGordon Ross smb_sdrc_t
smb2_write(smb_request_t * sr)27a90cf9f2SGordon Ross smb2_write(smb_request_t *sr)
28a90cf9f2SGordon Ross {
2993bc28dbSGordon Ross 	smb_rw_param_t *param = NULL;
30a90cf9f2SGordon Ross 	smb_ofile_t *of = NULL;
31a90cf9f2SGordon Ross 	smb_vdb_t *vdb = NULL;
32a90cf9f2SGordon Ross 	uint16_t StructSize;
33a90cf9f2SGordon Ross 	uint16_t DataOff;
34a90cf9f2SGordon Ross 	uint32_t Length;
35a90cf9f2SGordon Ross 	uint64_t Offset;
36a90cf9f2SGordon Ross 	smb2fid_t smb2fid;
37a90cf9f2SGordon Ross 	uint32_t Channel;
38a90cf9f2SGordon Ross 	uint32_t Remaining;
39a90cf9f2SGordon Ross 	uint16_t ChanInfoOffset;
40a90cf9f2SGordon Ross 	uint16_t ChanInfoLength;
41a90cf9f2SGordon Ross 	uint32_t Flags;
42a90cf9f2SGordon Ross 	uint32_t XferCount;
43a90cf9f2SGordon Ross 	uint32_t status;
44a90cf9f2SGordon Ross 	int data_chain_off, skip;
45a90cf9f2SGordon Ross 	int stability = 0;
46a90cf9f2SGordon Ross 	int rc = 0;
47dfa42fabSMatt Barden 	boolean_t unbuffered = B_FALSE;
48a90cf9f2SGordon Ross 
49a90cf9f2SGordon Ross 	/*
5093bc28dbSGordon Ross 	 * Decode SMB2 Write request
51a90cf9f2SGordon Ross 	 */
52a90cf9f2SGordon Ross 	rc = smb_mbc_decodef(
53a90cf9f2SGordon Ross 	    &sr->smb_data,
54a90cf9f2SGordon Ross 	    "wwlqqqllwwl",
55a90cf9f2SGordon Ross 	    &StructSize,		/* w */
56a90cf9f2SGordon Ross 	    &DataOff,			/* w */
57a90cf9f2SGordon Ross 	    &Length,			/* l */
58a90cf9f2SGordon Ross 	    &Offset,			/* q */
59a90cf9f2SGordon Ross 	    &smb2fid.persistent,	/* q */
60a90cf9f2SGordon Ross 	    &smb2fid.temporal,		/* q */
61a90cf9f2SGordon Ross 	    &Channel,			/* l */
62a90cf9f2SGordon Ross 	    &Remaining,			/* l */
63a90cf9f2SGordon Ross 	    &ChanInfoOffset,		/* w */
64a90cf9f2SGordon Ross 	    &ChanInfoLength,		/* w */
65a90cf9f2SGordon Ross 	    &Flags);			/* l */
66a90cf9f2SGordon Ross 	if (rc)
67a90cf9f2SGordon Ross 		return (SDRC_ERROR);
68a90cf9f2SGordon Ross 	if (StructSize != 49)
69a90cf9f2SGordon Ross 		return (SDRC_ERROR);
70a90cf9f2SGordon Ross 
7193bc28dbSGordon Ross 	/*
7293bc28dbSGordon Ross 	 * Setup an smb_rw_param_t which contains the VDB we need.
7393bc28dbSGordon Ross 	 * This is automatically free'd.
7493bc28dbSGordon Ross 	 */
7593bc28dbSGordon Ross 	param = smb_srm_zalloc(sr, sizeof (*param));
7693bc28dbSGordon Ross 	param->rw_offset = Offset;
7793bc28dbSGordon Ross 	param->rw_count = Length;
7893bc28dbSGordon Ross 	/* Note that the dtrace provider uses sr->arg.rw */
7993bc28dbSGordon Ross 	sr->arg.rw = param;
80a90cf9f2SGordon Ross 
81a90cf9f2SGordon Ross 	/*
82a90cf9f2SGordon Ross 	 * Skip any padding before the write data.
83a90cf9f2SGordon Ross 	 */
84a90cf9f2SGordon Ross 	data_chain_off = sr->smb2_cmd_hdr + DataOff;
85a90cf9f2SGordon Ross 	skip = data_chain_off - sr->smb_data.chain_offset;
8693bc28dbSGordon Ross 	if (skip < 0)
8793bc28dbSGordon Ross 		return (SDRC_ERROR);
8893bc28dbSGordon Ross 	if (skip > 0)
89a90cf9f2SGordon Ross 		(void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
90a90cf9f2SGordon Ross 
9193bc28dbSGordon Ross 	/*
9293bc28dbSGordon Ross 	 * Decode the write data (payload)
9393bc28dbSGordon Ross 	 */
9493bc28dbSGordon Ross 	if (Length > smb2_max_rwsize)
9593bc28dbSGordon Ross 		return (SDRC_ERROR);
9693bc28dbSGordon Ross 	vdb = &param->rw_vdb;
97a90cf9f2SGordon Ross 	rc = smb_mbc_decodef(&sr->smb_data, "#B", Length, vdb);
9893bc28dbSGordon Ross 	if (rc != 0 || vdb->vdb_len != Length)
9993bc28dbSGordon Ross 		return (SDRC_ERROR);
100a90cf9f2SGordon Ross 	vdb->vdb_uio.uio_loffset = (offset_t)Offset;
101a90cf9f2SGordon Ross 
10293bc28dbSGordon Ross 	/*
10393bc28dbSGordon Ross 	 * Want FID lookup before the start probe.
10493bc28dbSGordon Ross 	 */
10593bc28dbSGordon Ross 	status = smb2sr_lookup_fid(sr, &smb2fid);
10693bc28dbSGordon Ross 	of = sr->fid_ofile;
10793bc28dbSGordon Ross 
10893bc28dbSGordon Ross 	DTRACE_SMB2_START(op__Write, smb_request_t *, sr); /* arg.rw */
10993bc28dbSGordon Ross 
11093bc28dbSGordon Ross 	if (status)
11193bc28dbSGordon Ross 		goto errout; /* Bad FID */
11293bc28dbSGordon Ross 
11393bc28dbSGordon Ross 
114a90cf9f2SGordon Ross 	XferCount = 0;
115a90cf9f2SGordon Ross 	if (Length == 0)
11693bc28dbSGordon Ross 		goto errout;
117a90cf9f2SGordon Ross 
118dfa42fabSMatt Barden 	/*
119dfa42fabSMatt Barden 	 * Unbuffered refers to the MS-FSA Write argument by the same name.
120dfa42fabSMatt Barden 	 * It indicates that the cache for this range should be flushed to disk,
121dfa42fabSMatt Barden 	 * and data written directly to disk, bypassing the cache.
122dfa42fabSMatt Barden 	 * We don't allow that degree of cache management.
123dfa42fabSMatt Barden 	 * Translate this directly as FSYNC,
124dfa42fabSMatt Barden 	 * which should at least flush the cache.
125dfa42fabSMatt Barden 	 */
126dfa42fabSMatt Barden 
127dfa42fabSMatt Barden 	if (smb_allow_unbuffered &&
128dfa42fabSMatt Barden 	    (Flags & SMB2_WRITEFLAG_WRITE_UNBUFFERED) != 0)
129dfa42fabSMatt Barden 		unbuffered = B_TRUE;
130dfa42fabSMatt Barden 
131a90cf9f2SGordon Ross 	switch (of->f_tree->t_res_type & STYPE_MASK) {
132a90cf9f2SGordon Ross 	case STYPE_DISKTREE:
133a90cf9f2SGordon Ross 	case STYPE_PRINTQ:
134a90cf9f2SGordon Ross 		if (!smb_node_is_dir(of->f_node)) {
135a90cf9f2SGordon Ross 			/* Check for conflicting locks. */
136a90cf9f2SGordon Ross 			rc = smb_lock_range_access(sr, of->f_node,
137a90cf9f2SGordon Ross 			    Offset, Length, B_TRUE);
138a90cf9f2SGordon Ross 			if (rc) {
139a90cf9f2SGordon Ross 				rc = ERANGE;
140a90cf9f2SGordon Ross 				break;
141a90cf9f2SGordon Ross 			}
142a90cf9f2SGordon Ross 		}
143dfa42fabSMatt Barden 
144dfa42fabSMatt Barden 		if (unbuffered || (Flags & SMB2_WRITEFLAG_WRITE_THROUGH) != 0 ||
145dfa42fabSMatt Barden 		    (of->f_node->flags & NODE_FLAGS_WRITE_THROUGH) != 0) {
146a90cf9f2SGordon Ross 			stability = FSYNC;
147a90cf9f2SGordon Ross 		}
14855f0a249SGordon Ross 		rc = smb_fsop_write(sr, of->f_cr, of->f_node, of,
149a90cf9f2SGordon Ross 		    &vdb->vdb_uio, &XferCount, stability);
150a90cf9f2SGordon Ross 		if (rc)
151a90cf9f2SGordon Ross 			break;
15294047d49SGordon Ross 		/* This revokes read cache delegations. */
15394047d49SGordon Ross 		(void) smb_oplock_break_WRITE(of->f_node, of);
154*f8e30ca2SGordon Ross 		/*
155*f8e30ca2SGordon Ross 		 * Don't want the performance cost of generating
156*f8e30ca2SGordon Ross 		 * change notify events on every write.  Instead:
157*f8e30ca2SGordon Ross 		 * Keep track of the fact that we have written
158*f8e30ca2SGordon Ross 		 * data via this handle, and do change notify
159*f8e30ca2SGordon Ross 		 * work on the first write, and during close.
160*f8e30ca2SGordon Ross 		 */
161*f8e30ca2SGordon Ross 		if (of->f_written == B_FALSE) {
162*f8e30ca2SGordon Ross 			of->f_written = B_TRUE;
163*f8e30ca2SGordon Ross 			smb_node_notify_modified(of->f_node);
164*f8e30ca2SGordon Ross 		}
165a90cf9f2SGordon Ross 		break;
166a90cf9f2SGordon Ross 
167a90cf9f2SGordon Ross 	case STYPE_IPC:
168dfa42fabSMatt Barden 		if (unbuffered || (Flags & SMB2_WRITEFLAG_WRITE_THROUGH) != 0)
169dfa42fabSMatt Barden 			rc = EINVAL;
170dfa42fabSMatt Barden 		else
171a90cf9f2SGordon Ross 			rc = smb_opipe_write(sr, &vdb->vdb_uio);
172a90cf9f2SGordon Ross 		if (rc == 0)
173a90cf9f2SGordon Ross 			XferCount = Length;
174a90cf9f2SGordon Ross 		break;
175a90cf9f2SGordon Ross 
176a90cf9f2SGordon Ross 	default:
177a90cf9f2SGordon Ross 		rc = EACCES;
178a90cf9f2SGordon Ross 		break;
179a90cf9f2SGordon Ross 	}
18093bc28dbSGordon Ross 	status = smb_errno2status(rc);
181a90cf9f2SGordon Ross 
18293bc28dbSGordon Ross errout:
18393bc28dbSGordon Ross 	sr->smb2_status = status;
18493bc28dbSGordon Ross 	DTRACE_SMB2_DONE(op__Write, smb_request_t *, sr); /* arg.rw */
18593bc28dbSGordon Ross 
18693bc28dbSGordon Ross 	if (status) {
18793bc28dbSGordon Ross 		smb2sr_put_error(sr, status);
188a90cf9f2SGordon Ross 		return (SDRC_SUCCESS);
189a90cf9f2SGordon Ross 	}
190a90cf9f2SGordon Ross 
191a90cf9f2SGordon Ross 	/*
19293bc28dbSGordon Ross 	 * Encode SMB2 Write reply
193a90cf9f2SGordon Ross 	 */
194a90cf9f2SGordon Ross 	DataOff = SMB2_HDR_SIZE + 16;
195a90cf9f2SGordon Ross 	rc = smb_mbc_encodef(
196a90cf9f2SGordon Ross 	    &sr->reply, "wwlll",
197a90cf9f2SGordon Ross 	    17,	/* StructSize */	/* w */
198a90cf9f2SGordon Ross 	    0, /* reserved */		/* w */
199a90cf9f2SGordon Ross 	    XferCount,			/* l */
200a90cf9f2SGordon Ross 	    0, /* DataRemaining */	/* l */
201a90cf9f2SGordon Ross 	    0); /* Channel Info */	/* l */
20293bc28dbSGordon Ross 	if (rc) {
20393bc28dbSGordon Ross 		sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
204a90cf9f2SGordon Ross 		return (SDRC_ERROR);
20593bc28dbSGordon Ross 	}
206a90cf9f2SGordon Ross 
207a90cf9f2SGordon Ross 	mutex_enter(&of->f_mutex);
208a90cf9f2SGordon Ross 	of->f_seek_pos = Offset + XferCount;
209a90cf9f2SGordon Ross 	mutex_exit(&of->f_mutex);
210a90cf9f2SGordon Ross 
211a90cf9f2SGordon Ross 	return (SDRC_SUCCESS);
212a90cf9f2SGordon Ross }
213