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 = ¶m->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