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
smb2_write(smb_request_t * sr)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