1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21148c5f43SAlan Wright 
22da6c28aaSamw /*
23148c5f43SAlan Wright  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24a8e9db1cSGordon Ross  * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
25*f8e30ca2SGordon Ross  * Copyright 2022 RackTop Systems, Inc.
26da6c28aaSamw  */
27da6c28aaSamw 
28da6c28aaSamw #include <sys/sdt.h>
29bbf6f00cSJordan Brown #include <smbsrv/smb_kproto.h>
30da6c28aaSamw #include <smbsrv/smb_fsops.h>
31da6c28aaSamw #include <smbsrv/netbios.h>
32da6c28aaSamw 
33da6c28aaSamw 
34faa1795aSjb static int smb_write_truncate(smb_request_t *, smb_rw_param_t *);
35da6c28aaSamw 
36da6c28aaSamw 
37da6c28aaSamw /*
38da6c28aaSamw  * Write count bytes at the specified offset in a file.  The offset is
39da6c28aaSamw  * limited to 32-bits.  If the count is zero, the file is truncated to
40da6c28aaSamw  * the length specified by the offset.
41da6c28aaSamw  *
42da6c28aaSamw  * The response count indicates the actual number of bytes written, which
43da6c28aaSamw  * will equal the requested count on success.  If request and response
44da6c28aaSamw  * counts differ but there is no error, the client will assume that the
45da6c28aaSamw  * server encountered a resource issue.
46da6c28aaSamw  */
477b59d02dSjb smb_sdrc_t
smb_pre_write(smb_request_t * sr)48faa1795aSjb smb_pre_write(smb_request_t *sr)
49da6c28aaSamw {
50faa1795aSjb 	smb_rw_param_t *param;
51da6c28aaSamw 	uint32_t off;
522c2961f8Sjose borrego 	uint16_t count;
53da6c28aaSamw 	int rc;
54da6c28aaSamw 
55faa1795aSjb 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
56faa1795aSjb 	sr->arg.rw = param;
57faa1795aSjb 	param->rw_magic = SMB_RW_MAGIC;
58da6c28aaSamw 
592c2961f8Sjose borrego 	rc = smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, &count, &off);
60faa1795aSjb 
612c2961f8Sjose borrego 	param->rw_count = (uint32_t)count;
62faa1795aSjb 	param->rw_offset = (uint64_t)off;
632c2961f8Sjose borrego 	param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
64faa1795aSjb 
6593bc28dbSGordon Ross 	DTRACE_SMB_START(op__Write, smb_request_t *, sr); /* arg.rw */
66faa1795aSjb 
67faa1795aSjb 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
68faa1795aSjb }
69faa1795aSjb 
70faa1795aSjb void
smb_post_write(smb_request_t * sr)71faa1795aSjb smb_post_write(smb_request_t *sr)
72faa1795aSjb {
7393bc28dbSGordon Ross 	DTRACE_SMB_DONE(op__Write, smb_request_t *, sr); /* arg.rw */
74faa1795aSjb 
75faa1795aSjb 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
76faa1795aSjb }
77faa1795aSjb 
78faa1795aSjb smb_sdrc_t
smb_com_write(smb_request_t * sr)79faa1795aSjb smb_com_write(smb_request_t *sr)
80faa1795aSjb {
81faa1795aSjb 	smb_rw_param_t *param = sr->arg.rw;
82faa1795aSjb 	int rc;
83da6c28aaSamw 
842c2961f8Sjose borrego 	smbsr_lookup_file(sr);
85da6c28aaSamw 	if (sr->fid_ofile == NULL) {
86dc20a302Sas 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
87faa1795aSjb 		return (SDRC_ERROR);
88da6c28aaSamw 	}
89da6c28aaSamw 
90b89a8333Snatalie li - Sun Microsystems - Irvine United States 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
91b89a8333Snatalie li - Sun Microsystems - Irvine United States 
92faa1795aSjb 	if (param->rw_count == 0) {
93da6c28aaSamw 		rc = smb_write_truncate(sr, param);
94da6c28aaSamw 	} else {
95faa1795aSjb 		rc = smbsr_decode_data(sr, "D", &param->rw_vdb);
96da6c28aaSamw 
972c2961f8Sjose borrego 		if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
98faa1795aSjb 			smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
99faa1795aSjb 			    ERRDOS, ERROR_INVALID_PARAMETER);
100faa1795aSjb 			return (SDRC_ERROR);
101da6c28aaSamw 		}
102da6c28aaSamw 
1032c2961f8Sjose borrego 		param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
104da6c28aaSamw 
105f96bd5c8SAlan Wright 		rc = smb_common_write(sr, param);
106da6c28aaSamw 	}
107da6c28aaSamw 
108da6c28aaSamw 	if (rc != 0) {
109faa1795aSjb 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
110faa1795aSjb 			smbsr_errno(sr, rc);
111faa1795aSjb 		return (SDRC_ERROR);
112da6c28aaSamw 	}
113da6c28aaSamw 
1142c2961f8Sjose borrego 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
1152c2961f8Sjose borrego 	    (uint16_t)param->rw_count, 0);
116faa1795aSjb 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
117da6c28aaSamw }
118da6c28aaSamw 
119da6c28aaSamw /*
120da6c28aaSamw  * Write count bytes to a file and then close the file.  This function
121da6c28aaSamw  * can only be used to write to 32-bit offsets and the client must set
122da6c28aaSamw  * WordCount (6 or 12) correctly in order to locate the data to be
123da6c28aaSamw  * written.  If an error occurs on the write, the file should still be
124da6c28aaSamw  * closed.  If Count is 0, the file is truncated (or extended) to offset.
125da6c28aaSamw  *
126da6c28aaSamw  * If the last_write time is non-zero, last_write should be used to set
127da6c28aaSamw  * the mtime.  Otherwise the file system stamps the mtime.  Failure to
128da6c28aaSamw  * set mtime should not result in an error response.
129da6c28aaSamw  */
1307b59d02dSjb smb_sdrc_t
smb_pre_write_and_close(smb_request_t * sr)131faa1795aSjb smb_pre_write_and_close(smb_request_t *sr)
132da6c28aaSamw {
133faa1795aSjb 	smb_rw_param_t *param;
134da6c28aaSamw 	uint32_t off;
1352c2961f8Sjose borrego 	uint16_t count;
136faa1795aSjb 	int rc;
137da6c28aaSamw 
138faa1795aSjb 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
139faa1795aSjb 	sr->arg.rw = param;
140faa1795aSjb 	param->rw_magic = SMB_RW_MAGIC;
141da6c28aaSamw 
142da6c28aaSamw 	if (sr->smb_wct == 12) {
143da6c28aaSamw 		rc = smbsr_decode_vwv(sr, "wwll12.", &sr->smb_fid,
1442c2961f8Sjose borrego 		    &count, &off, &param->rw_last_write);
145da6c28aaSamw 	} else {
146da6c28aaSamw 		rc = smbsr_decode_vwv(sr, "wwll", &sr->smb_fid,
1472c2961f8Sjose borrego 		    &count, &off, &param->rw_last_write);
148da6c28aaSamw 	}
149da6c28aaSamw 
1502c2961f8Sjose borrego 	param->rw_count = (uint32_t)count;
151faa1795aSjb 	param->rw_offset = (uint64_t)off;
152faa1795aSjb 
15393bc28dbSGordon Ross 	DTRACE_SMB_START(op__WriteAndClose, smb_request_t *, sr); /* arg.rw */
154faa1795aSjb 
155faa1795aSjb 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
156faa1795aSjb }
157faa1795aSjb 
158faa1795aSjb void
smb_post_write_and_close(smb_request_t * sr)159faa1795aSjb smb_post_write_and_close(smb_request_t *sr)
160faa1795aSjb {
16193bc28dbSGordon Ross 	DTRACE_SMB_DONE(op__WriteAndClose, smb_request_t *, sr); /* arg.rw */
162faa1795aSjb 
163faa1795aSjb 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
164faa1795aSjb }
165faa1795aSjb 
166faa1795aSjb smb_sdrc_t
smb_com_write_and_close(smb_request_t * sr)167faa1795aSjb smb_com_write_and_close(smb_request_t *sr)
168faa1795aSjb {
169faa1795aSjb 	smb_rw_param_t *param = sr->arg.rw;
1702c2961f8Sjose borrego 	uint16_t count;
171faa1795aSjb 	int rc = 0;
172da6c28aaSamw 
1732c2961f8Sjose borrego 	smbsr_lookup_file(sr);
174da6c28aaSamw 	if (sr->fid_ofile == NULL) {
175dc20a302Sas 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
176faa1795aSjb 		return (SDRC_ERROR);
177da6c28aaSamw 	}
178da6c28aaSamw 
179b89a8333Snatalie li - Sun Microsystems - Irvine United States 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
180b89a8333Snatalie li - Sun Microsystems - Irvine United States 
181faa1795aSjb 	if (param->rw_count == 0) {
182da6c28aaSamw 		rc = smb_write_truncate(sr, param);
183da6c28aaSamw 	} else {
184da6c28aaSamw 		/*
185da6c28aaSamw 		 * There may be a bug here: should this be "3.#B"?
186da6c28aaSamw 		 */
187faa1795aSjb 		rc = smbsr_decode_data(sr, ".#B", param->rw_count,
188faa1795aSjb 		    &param->rw_vdb);
189da6c28aaSamw 
1902c2961f8Sjose borrego 		if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
191faa1795aSjb 			smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
192faa1795aSjb 			    ERRDOS, ERROR_INVALID_PARAMETER);
193faa1795aSjb 			return (SDRC_ERROR);
194da6c28aaSamw 		}
195da6c28aaSamw 
1962c2961f8Sjose borrego 		param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
197da6c28aaSamw 
198f96bd5c8SAlan Wright 		rc = smb_common_write(sr, param);
199da6c28aaSamw 	}
200da6c28aaSamw 
201da6c28aaSamw 	if (rc != 0) {
202faa1795aSjb 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
203faa1795aSjb 			smbsr_errno(sr, rc);
204faa1795aSjb 		return (SDRC_ERROR);
205da6c28aaSamw 	}
206da6c28aaSamw 
207c8ec8eeaSjose borrego 	smb_ofile_close(sr->fid_ofile, param->rw_last_write);
208da6c28aaSamw 
2092c2961f8Sjose borrego 	count = (uint16_t)param->rw_count;
2102c2961f8Sjose borrego 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1, count, 0);
211faa1795aSjb 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
212da6c28aaSamw }
213da6c28aaSamw 
214da6c28aaSamw /*
215da6c28aaSamw  * Write count bytes to a file at the specified offset and then unlock
216da6c28aaSamw  * them.  Write behind is safe because the client should have the range
217da6c28aaSamw  * locked and this request is allowed to extend the file - note that
218faa1795aSjb  * offset is limited to 32-bits.
219faa1795aSjb  *
220faa1795aSjb  * Spec advice: it is an error for count to be zero.  For compatibility,
221faa1795aSjb  * we take no action and return success.
222da6c28aaSamw  *
223da6c28aaSamw  * The SmbLockAndRead/SmbWriteAndUnlock sub-dialect is only valid on disk
224da6c28aaSamw  * files.  Reject any attempt to use it on other shares.
225da6c28aaSamw  *
226da6c28aaSamw  * The response count indicates the actual number of bytes written, which
227da6c28aaSamw  * will equal the requested count on success.  If request and response
228da6c28aaSamw  * counts differ but there is no error, the client will assume that the
229da6c28aaSamw  * server encountered a resource issue.
230da6c28aaSamw  */
2317b59d02dSjb smb_sdrc_t
smb_pre_write_and_unlock(smb_request_t * sr)232faa1795aSjb smb_pre_write_and_unlock(smb_request_t *sr)
233da6c28aaSamw {
234faa1795aSjb 	smb_rw_param_t *param;
235da6c28aaSamw 	uint32_t off;
2362c2961f8Sjose borrego 	uint16_t count;
237da6c28aaSamw 	uint16_t remcnt;
238faa1795aSjb 	int rc;
239faa1795aSjb 
240faa1795aSjb 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
241faa1795aSjb 	sr->arg.rw = param;
242faa1795aSjb 	param->rw_magic = SMB_RW_MAGIC;
243faa1795aSjb 
2442c2961f8Sjose borrego 	rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, &count, &off, &remcnt);
245faa1795aSjb 
2462c2961f8Sjose borrego 	param->rw_count = (uint32_t)count;
247faa1795aSjb 	param->rw_offset = (uint64_t)off;
248faa1795aSjb 
24993bc28dbSGordon Ross 	DTRACE_SMB_START(op__WriteAndUnlock, smb_request_t *, sr); /* arg.rw */
250faa1795aSjb 
251faa1795aSjb 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
252faa1795aSjb }
253faa1795aSjb 
254faa1795aSjb void
smb_post_write_and_unlock(smb_request_t * sr)255faa1795aSjb smb_post_write_and_unlock(smb_request_t *sr)
256faa1795aSjb {
25793bc28dbSGordon Ross 	DTRACE_SMB_DONE(op__WriteAndUnlock, smb_request_t *, sr); /* arg.rw */
258faa1795aSjb 
259faa1795aSjb 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
260faa1795aSjb }
261faa1795aSjb 
262faa1795aSjb smb_sdrc_t
smb_com_write_and_unlock(smb_request_t * sr)263faa1795aSjb smb_com_write_and_unlock(smb_request_t *sr)
264faa1795aSjb {
265faa1795aSjb 	smb_rw_param_t *param = sr->arg.rw;
2660897f7fbSGordon Ross 	uint32_t lk_pid;
267faa1795aSjb 	uint32_t status;
268da6c28aaSamw 	int rc = 0;
269da6c28aaSamw 
270da6c28aaSamw 	if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
271dc20a302Sas 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
272faa1795aSjb 		return (SDRC_ERROR);
273da6c28aaSamw 	}
274da6c28aaSamw 
2752c2961f8Sjose borrego 	smbsr_lookup_file(sr);
276da6c28aaSamw 	if (sr->fid_ofile == NULL) {
277dc20a302Sas 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
278faa1795aSjb 		return (SDRC_ERROR);
279da6c28aaSamw 	}
280da6c28aaSamw 
281b89a8333Snatalie li - Sun Microsystems - Irvine United States 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
282b89a8333Snatalie li - Sun Microsystems - Irvine United States 
283faa1795aSjb 	if (param->rw_count == 0) {
284faa1795aSjb 		rc = smbsr_encode_result(sr, 1, 0, "bww", 1, 0, 0);
285faa1795aSjb 		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
286da6c28aaSamw 	}
287da6c28aaSamw 
2882c2961f8Sjose borrego 
289faa1795aSjb 	rc = smbsr_decode_data(sr, "D", &param->rw_vdb);
290da6c28aaSamw 
2912c2961f8Sjose borrego 	if ((rc != 0) || (param->rw_count != param->rw_vdb.vdb_len)) {
292faa1795aSjb 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
293faa1795aSjb 		    ERRDOS, ERROR_INVALID_PARAMETER);
294faa1795aSjb 		return (SDRC_ERROR);
295da6c28aaSamw 	}
296da6c28aaSamw 
2972c2961f8Sjose borrego 	param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
298da6c28aaSamw 
299f96bd5c8SAlan Wright 	if ((rc = smb_common_write(sr, param)) != 0) {
300faa1795aSjb 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
301faa1795aSjb 			smbsr_errno(sr, rc);
302faa1795aSjb 		return (SDRC_ERROR);
303da6c28aaSamw 	}
304da6c28aaSamw 
3050897f7fbSGordon Ross 
3060897f7fbSGordon Ross 	/* Note: SMB1 locking uses 16-bit PIDs. */
3070897f7fbSGordon Ross 	lk_pid = sr->smb_pid & 0xFFFF;
3080897f7fbSGordon Ross 
3090897f7fbSGordon Ross 	status = smb_unlock_range(sr, param->rw_offset,
3100897f7fbSGordon Ross 	    (uint64_t)param->rw_count, lk_pid);
311faa1795aSjb 	if (status != NT_STATUS_SUCCESS) {
312dc20a302Sas 		smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
313148c5f43SAlan Wright 		    ERRDOS, ERROR_NOT_LOCKED);
314faa1795aSjb 		return (SDRC_ERROR);
315da6c28aaSamw 	}
316da6c28aaSamw 
3172c2961f8Sjose borrego 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
3182c2961f8Sjose borrego 	    (uint16_t)param->rw_count, 0);
319faa1795aSjb 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
320da6c28aaSamw }
321da6c28aaSamw 
32293bc28dbSGordon Ross /*
32393bc28dbSGordon Ross  * The SMB_COM_WRITE_RAW protocol was a negotiated option introduced in
32493bc28dbSGordon Ross  * SMB Core Plus to maximize performance when writing a large block
32593bc28dbSGordon Ross  * of data to a server.  It's obsolete and no longer supported.
32693bc28dbSGordon Ross  *
32793bc28dbSGordon Ross  * We keep a handler for it so the dtrace provider can see if
32893bc28dbSGordon Ross  * the client tried to use this command.
32993bc28dbSGordon Ross  */
33093bc28dbSGordon Ross smb_sdrc_t
smb_pre_write_raw(smb_request_t * sr)33193bc28dbSGordon Ross smb_pre_write_raw(smb_request_t *sr)
33293bc28dbSGordon Ross {
33393bc28dbSGordon Ross 	DTRACE_SMB_START(op__WriteRaw, smb_request_t *, sr);
33493bc28dbSGordon Ross 	return (SDRC_SUCCESS);
33593bc28dbSGordon Ross }
33693bc28dbSGordon Ross 
33793bc28dbSGordon Ross void
smb_post_write_raw(smb_request_t * sr)33893bc28dbSGordon Ross smb_post_write_raw(smb_request_t *sr)
33993bc28dbSGordon Ross {
34093bc28dbSGordon Ross 	DTRACE_SMB_DONE(op__WriteRaw, smb_request_t *, sr);
34193bc28dbSGordon Ross }
34293bc28dbSGordon Ross 
34393bc28dbSGordon Ross smb_sdrc_t
smb_com_write_raw(struct smb_request * sr)34493bc28dbSGordon Ross smb_com_write_raw(struct smb_request *sr)
34593bc28dbSGordon Ross {
34693bc28dbSGordon Ross 	smbsr_error(sr, NT_STATUS_NOT_SUPPORTED, ERRDOS,
34793bc28dbSGordon Ross 	    ERROR_NOT_SUPPORTED);
34893bc28dbSGordon Ross 	return (SDRC_ERROR);
34993bc28dbSGordon Ross }
35093bc28dbSGordon Ross 
351da6c28aaSamw /*
352da6c28aaSamw  * Write bytes to a file (SMB Core).  This request was extended in
353da6c28aaSamw  * LM 0.12 to support 64-bit offsets, indicated by sending a wct of
354da6c28aaSamw  * 14, instead of 12, and including additional offset information.
355da6c28aaSamw  *
356da6c28aaSamw  * A ByteCount of 0 does not truncate the file - use SMB_COM_WRITE
357da6c28aaSamw  * to truncate a file.  A zero length merely transfers zero bytes.
358da6c28aaSamw  *
359da6c28aaSamw  * If bit 0 of WriteMode is set, Fid must refer to a disk file and
360da6c28aaSamw  * the data must be on stable storage before responding.
3612c2961f8Sjose borrego  *
3622c2961f8Sjose borrego  * MS-SMB 3.3.5.8 update to LM 0.12 4.2.5:
3632c2961f8Sjose borrego  * If CAP_LARGE_WRITEX is set, the byte count may be larger than the
3642c2961f8Sjose borrego  * negotiated buffer size and the server is expected to write the
3652c2961f8Sjose borrego  * number of bytes specified.
366da6c28aaSamw  */
3677b59d02dSjb smb_sdrc_t
smb_pre_write_andx(smb_request_t * sr)368faa1795aSjb smb_pre_write_andx(smb_request_t *sr)
369da6c28aaSamw {
370faa1795aSjb 	smb_rw_param_t *param;
371da6c28aaSamw 	uint32_t off_low;
372da6c28aaSamw 	uint32_t off_high;
3732c2961f8Sjose borrego 	uint16_t datalen_low;
3742c2961f8Sjose borrego 	uint16_t datalen_high;
375da6c28aaSamw 	uint16_t remcnt;
376faa1795aSjb 	int rc;
377da6c28aaSamw 
378faa1795aSjb 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
379faa1795aSjb 	sr->arg.rw = param;
380faa1795aSjb 	param->rw_magic = SMB_RW_MAGIC;
381da6c28aaSamw 
382da6c28aaSamw 	if (sr->smb_wct == 14) {
3832c2961f8Sjose borrego 		rc = smbsr_decode_vwv(sr, "4.wl4.wwwwwl", &sr->smb_fid,
3842c2961f8Sjose borrego 		    &off_low, &param->rw_mode, &remcnt, &datalen_high,
3852c2961f8Sjose borrego 		    &datalen_low, &param->rw_dsoff, &off_high);
386da6c28aaSamw 
3871fb4a876SGordon Ross 		if (param->rw_dsoff >= 63)
3881fb4a876SGordon Ross 			param->rw_dsoff -= 63;
389faa1795aSjb 		param->rw_offset = ((uint64_t)off_high << 32) | off_low;
3901fb4a876SGordon Ross 	} else if (sr->smb_wct == 12) {
3912c2961f8Sjose borrego 		rc = smbsr_decode_vwv(sr, "4.wl4.wwwww", &sr->smb_fid,
3922c2961f8Sjose borrego 		    &off_low, &param->rw_mode, &remcnt, &datalen_high,
3932c2961f8Sjose borrego 		    &datalen_low, &param->rw_dsoff);
394da6c28aaSamw 
3951fb4a876SGordon Ross 		if (param->rw_dsoff >= 59)
3961fb4a876SGordon Ross 			param->rw_dsoff -= 59;
397faa1795aSjb 		param->rw_offset = (uint64_t)off_low;
3981fb4a876SGordon Ross 		/* off_high not present */
3991fb4a876SGordon Ross 	} else {
4001fb4a876SGordon Ross 		rc = -1;
401da6c28aaSamw 	}
402da6c28aaSamw 
4033a6c5f83SAlan Wright 	param->rw_count = (uint32_t)datalen_low;
4043a6c5f83SAlan Wright 
4051fb4a876SGordon Ross 	/*
4061fb4a876SGordon Ross 	 * Work-around a Win7 bug, where it fails to set the
4071fb4a876SGordon Ross 	 * CAP_LARGE_WRITEX flag during session setup.  Assume
4081fb4a876SGordon Ross 	 * a large write if the data remaining is >= 64k.
4091fb4a876SGordon Ross 	 */
4101fb4a876SGordon Ross 	if ((sr->session->capabilities & CAP_LARGE_WRITEX) != 0 ||
4111fb4a876SGordon Ross 	    (sr->smb_data.max_bytes > (sr->smb_data.chain_offset + 0xFFFF)))
4123a6c5f83SAlan Wright 		param->rw_count |= ((uint32_t)datalen_high << 16);
4132c2961f8Sjose borrego 
41493bc28dbSGordon Ross 	DTRACE_SMB_START(op__WriteX, smb_request_t *, sr); /* arg.rw */
415faa1795aSjb 
416faa1795aSjb 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
417faa1795aSjb }
418faa1795aSjb 
419faa1795aSjb void
smb_post_write_andx(smb_request_t * sr)420faa1795aSjb smb_post_write_andx(smb_request_t *sr)
421faa1795aSjb {
42293bc28dbSGordon Ross 	DTRACE_SMB_DONE(op__WriteX, smb_request_t *, sr); /* arg.rw */
423faa1795aSjb 
424faa1795aSjb 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
425faa1795aSjb }
426faa1795aSjb 
427faa1795aSjb smb_sdrc_t
smb_com_write_andx(smb_request_t * sr)428faa1795aSjb smb_com_write_andx(smb_request_t *sr)
429faa1795aSjb {
430faa1795aSjb 	smb_rw_param_t *param = sr->arg.rw;
4312c2961f8Sjose borrego 	uint16_t count_high;
4322c2961f8Sjose borrego 	uint16_t count_low;
433faa1795aSjb 	int rc;
434faa1795aSjb 
435faa1795aSjb 	ASSERT(param);
436faa1795aSjb 	ASSERT(param->rw_magic == SMB_RW_MAGIC);
437da6c28aaSamw 
4382c2961f8Sjose borrego 	smbsr_lookup_file(sr);
439da6c28aaSamw 	if (sr->fid_ofile == NULL) {
440dc20a302Sas 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
441faa1795aSjb 		return (SDRC_ERROR);
442da6c28aaSamw 	}
443da6c28aaSamw 
444b89a8333Snatalie li - Sun Microsystems - Irvine United States 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
445b89a8333Snatalie li - Sun Microsystems - Irvine United States 
446faa1795aSjb 	if (SMB_WRMODE_IS_STABLE(param->rw_mode) &&
447f96bd5c8SAlan Wright 	    STYPE_ISIPC(sr->tid_tree->t_res_type)) {
448dc20a302Sas 		smbsr_error(sr, 0, ERRSRV, ERRaccess);
449faa1795aSjb 		return (SDRC_ERROR);
450da6c28aaSamw 	}
451da6c28aaSamw 
452faa1795aSjb 	rc = smbsr_decode_data(sr, "#.#B", param->rw_dsoff, param->rw_count,
453faa1795aSjb 	    &param->rw_vdb);
4542c2961f8Sjose borrego 
4552c2961f8Sjose borrego 	if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
456faa1795aSjb 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
457faa1795aSjb 		    ERRDOS, ERROR_INVALID_PARAMETER);
458faa1795aSjb 		return (SDRC_ERROR);
459da6c28aaSamw 	}
460da6c28aaSamw 
4612c2961f8Sjose borrego 	param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
462da6c28aaSamw 
463faa1795aSjb 	if (param->rw_count != 0) {
464f96bd5c8SAlan Wright 		if ((rc = smb_common_write(sr, param)) != 0) {
465faa1795aSjb 			if (sr->smb_error.status !=
466faa1795aSjb 			    NT_STATUS_FILE_LOCK_CONFLICT)
467faa1795aSjb 				smbsr_errno(sr, rc);
468faa1795aSjb 			return (SDRC_ERROR);
469da6c28aaSamw 		}
470da6c28aaSamw 	}
471da6c28aaSamw 
4722c2961f8Sjose borrego 	count_low = param->rw_count & 0xFFFF;
4732c2961f8Sjose borrego 	count_high = (param->rw_count >> 16) & 0xFF;
4742c2961f8Sjose borrego 
4752c2961f8Sjose borrego 	rc = smbsr_encode_result(sr, 6, 0, "bb1.wwwwww",
4762c2961f8Sjose borrego 	    6, sr->andx_com, 15, count_low, 0, count_high, 0, 0);
477da6c28aaSamw 
478faa1795aSjb 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
479da6c28aaSamw }
480da6c28aaSamw 
481da6c28aaSamw /*
482da6c28aaSamw  * Common function for writing files or IPC/MSRPC named pipes.
483da6c28aaSamw  *
484da6c28aaSamw  * Returns errno values.
485da6c28aaSamw  */
486f96bd5c8SAlan Wright int
smb_common_write(smb_request_t * sr,smb_rw_param_t * param)487f96bd5c8SAlan Wright smb_common_write(smb_request_t *sr, smb_rw_param_t *param)
488da6c28aaSamw {
489037cac00Sjoyce mcintosh 	smb_ofile_t *ofile = sr->fid_ofile;
490da6c28aaSamw 	smb_node_t *node;
4913db3f65cSamw 	int stability = 0;
492da6c28aaSamw 	uint32_t lcount;
493da6c28aaSamw 	int rc = 0;
494da6c28aaSamw 
495da6c28aaSamw 	switch (sr->tid_tree->t_res_type & STYPE_MASK) {
496da6c28aaSamw 	case STYPE_DISKTREE:
497f96bd5c8SAlan Wright 	case STYPE_PRINTQ:
498da6c28aaSamw 		node = ofile->f_node;
499da6c28aaSamw 
500037cac00Sjoyce mcintosh 		if (!smb_node_is_dir(node)) {
501faa1795aSjb 			rc = smb_lock_range_access(sr, node, param->rw_offset,
502faa1795aSjb 			    param->rw_count, B_TRUE);
503faa1795aSjb 			if (rc != NT_STATUS_SUCCESS) {
504faa1795aSjb 				smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
505faa1795aSjb 				    ERRDOS, ERROR_LOCK_VIOLATION);
506faa1795aSjb 				return (EACCES);
507faa1795aSjb 			}
508da6c28aaSamw 		}
509da6c28aaSamw 
510faa1795aSjb 		if (SMB_WRMODE_IS_STABLE(param->rw_mode) ||
511da6c28aaSamw 		    (node->flags & NODE_FLAGS_WRITE_THROUGH)) {
5123db3f65cSamw 			stability = FSYNC;
513da6c28aaSamw 		}
514da6c28aaSamw 
51555f0a249SGordon Ross 		rc = smb_fsop_write(sr, sr->user_cr, node, ofile,
516037cac00Sjoyce mcintosh 		    &param->rw_vdb.vdb_uio, &lcount, stability);
517da6c28aaSamw 		if (rc)
518da6c28aaSamw 			return (rc);
519*f8e30ca2SGordon Ross 		param->rw_count = lcount;
52094047d49SGordon Ross 		/* This revokes read cache delegations. */
52194047d49SGordon Ross 		(void) smb_oplock_break_WRITE(node, ofile);
522*f8e30ca2SGordon Ross 		/*
523*f8e30ca2SGordon Ross 		 * Don't want the performance cost of generating
524*f8e30ca2SGordon Ross 		 * change notify events on every write.  Instead:
525*f8e30ca2SGordon Ross 		 * Keep track of the fact that we have written
526*f8e30ca2SGordon Ross 		 * data via this handle, and do change notify
527*f8e30ca2SGordon Ross 		 * work on the first write, and during close.
528*f8e30ca2SGordon Ross 		 */
529*f8e30ca2SGordon Ross 		if (ofile->f_written == B_FALSE) {
530*f8e30ca2SGordon Ross 			ofile->f_written = B_TRUE;
531*f8e30ca2SGordon Ross 			smb_node_notify_modified(node);
532*f8e30ca2SGordon Ross 		}
533da6c28aaSamw 		break;
534da6c28aaSamw 
535da6c28aaSamw 	case STYPE_IPC:
5362c2961f8Sjose borrego 		param->rw_count = param->rw_vdb.vdb_uio.uio_resid;
537da6c28aaSamw 
5382c2961f8Sjose borrego 		if ((rc = smb_opipe_write(sr, &param->rw_vdb.vdb_uio)) != 0)
539faa1795aSjb 			param->rw_count = 0;
540da6c28aaSamw 		break;
541da6c28aaSamw 
542da6c28aaSamw 	default:
543da6c28aaSamw 		rc = EACCES;
544da6c28aaSamw 		break;
545da6c28aaSamw 	}
546da6c28aaSamw 
547da6c28aaSamw 	if (rc != 0)
548da6c28aaSamw 		return (rc);
549da6c28aaSamw 
550da6c28aaSamw 	mutex_enter(&ofile->f_mutex);
551faa1795aSjb 	ofile->f_seek_pos = param->rw_offset + param->rw_count;
552da6c28aaSamw 	mutex_exit(&ofile->f_mutex);
553da6c28aaSamw 	return (rc);
554da6c28aaSamw }
555da6c28aaSamw 
556da6c28aaSamw /*
557da6c28aaSamw  * Truncate a disk file to the specified offset.
558da6c28aaSamw  * Typically, w_count will be zero here.
559da6c28aaSamw  *
560037cac00Sjoyce mcintosh  * Note that smb_write_andx cannot be used to reduce the file size so,
561037cac00Sjoyce mcintosh  * if this is required, smb_write is called with a count of zero and
562037cac00Sjoyce mcintosh  * the appropriate file length in offset. The file should be resized
563037cac00Sjoyce mcintosh  * to the length specified by the offset.
564037cac00Sjoyce mcintosh  *
565da6c28aaSamw  * Returns errno values.
566da6c28aaSamw  */
5677b59d02dSjb static int
smb_write_truncate(smb_request_t * sr,smb_rw_param_t * param)568faa1795aSjb smb_write_truncate(smb_request_t *sr, smb_rw_param_t *param)
569da6c28aaSamw {
570037cac00Sjoyce mcintosh 	smb_ofile_t *ofile = sr->fid_ofile;
571da6c28aaSamw 	smb_node_t *node = ofile->f_node;
572037cac00Sjoyce mcintosh 	smb_attr_t attr;
5737b59d02dSjb 	uint32_t status;
574da6c28aaSamw 	int rc;
575da6c28aaSamw 
576f96bd5c8SAlan Wright 	if (STYPE_ISIPC(sr->tid_tree->t_res_type))
577da6c28aaSamw 		return (0);
578da6c28aaSamw 
5792c2961f8Sjose borrego 	mutex_enter(&node->n_mutex);
580037cac00Sjoyce mcintosh 	if (!smb_node_is_dir(node)) {
581faa1795aSjb 		status = smb_lock_range_access(sr, node, param->rw_offset,
582faa1795aSjb 		    param->rw_count, B_TRUE);
5837b59d02dSjb 		if (status != NT_STATUS_SUCCESS) {
5842c2961f8Sjose borrego 			mutex_exit(&node->n_mutex);
585faa1795aSjb 			smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
586faa1795aSjb 			    ERRDOS, ERROR_LOCK_VIOLATION);
5877b59d02dSjb 			return (EACCES);
588dc20a302Sas 		}
589dc20a302Sas 	}
5902c2961f8Sjose borrego 	mutex_exit(&node->n_mutex);
591dc20a302Sas 
592037cac00Sjoyce mcintosh 	bzero(&attr, sizeof (smb_attr_t));
593037cac00Sjoyce mcintosh 	attr.sa_mask = SMB_AT_SIZE;
594037cac00Sjoyce mcintosh 	attr.sa_vattr.va_size = param->rw_offset;
595037cac00Sjoyce mcintosh 	rc = smb_node_setattr(sr, node, sr->user_cr, ofile, &attr);
596037cac00Sjoyce mcintosh 	if (rc != 0)
597da6c28aaSamw 		return (rc);
598da6c28aaSamw 
599da6c28aaSamw 	mutex_enter(&ofile->f_mutex);
600faa1795aSjb 	ofile->f_seek_pos = param->rw_offset + param->rw_count;
601da6c28aaSamw 	mutex_exit(&ofile->f_mutex);
602da6c28aaSamw 	return (0);
603da6c28aaSamw }
604