xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_write.c (revision d48be21240dfd051b689384ce2b23479d757f2d8)
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 = &param->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