xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_write.c (revision 6139:5c743b207bf9)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
225772Sas200622  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw 
265331Samw #pragma ident	"%Z%%M%	%I%	%E% SMI"
275331Samw 
285331Samw #include <sys/sdt.h>
295331Samw #include <smbsrv/smb_incl.h>
305331Samw #include <smbsrv/smb_fsops.h>
315331Samw #include <smbsrv/mbuf.h>
325331Samw #include <smbsrv/netbios.h>
335331Samw 
345331Samw 
355331Samw #define	SMB_WRMODE_WRITE_THRU	0x0001
365331Samw #define	SMB_WRMODE_IS_STABLE(M)	((M) & SMB_WRMODE_WRITE_THRU)
375331Samw 
385331Samw 
39*6139Sjb150015 static int smb_write_common(smb_request_t *, smb_rw_param_t *);
40*6139Sjb150015 static int smb_write_truncate(smb_request_t *, smb_rw_param_t *);
41*6139Sjb150015 int smb_set_file_size(smb_request_t *);
425331Samw 
435331Samw 
445331Samw /*
455331Samw  * Write count bytes at the specified offset in a file.  The offset is
465331Samw  * limited to 32-bits.  If the count is zero, the file is truncated to
475331Samw  * the length specified by the offset.
485331Samw  *
495331Samw  * The response count indicates the actual number of bytes written, which
505331Samw  * will equal the requested count on success.  If request and response
515331Samw  * counts differ but there is no error, the client will assume that the
525331Samw  * server encountered a resource issue.
535331Samw  */
546030Sjb150015 smb_sdrc_t
55*6139Sjb150015 smb_pre_write(smb_request_t *sr)
565331Samw {
57*6139Sjb150015 	smb_rw_param_t *param;
585331Samw 	uint32_t off;
595331Samw 	int rc;
605331Samw 
61*6139Sjb150015 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
62*6139Sjb150015 	sr->arg.rw = param;
63*6139Sjb150015 	param->rw_magic = SMB_RW_MAGIC;
64*6139Sjb150015 
65*6139Sjb150015 	rc = smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, &param->rw_count, &off);
66*6139Sjb150015 
67*6139Sjb150015 	param->rw_offset = (uint64_t)off;
68*6139Sjb150015 	param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
69*6139Sjb150015 
70*6139Sjb150015 	DTRACE_SMB_2(op__Write__start, smb_request_t *, sr,
71*6139Sjb150015 	    smb_rw_param_t *, param);
72*6139Sjb150015 
73*6139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
74*6139Sjb150015 }
755331Samw 
76*6139Sjb150015 void
77*6139Sjb150015 smb_post_write(smb_request_t *sr)
78*6139Sjb150015 {
79*6139Sjb150015 	DTRACE_SMB_2(op__Write__done, smb_request_t *, sr,
80*6139Sjb150015 	    smb_rw_param_t *, sr->arg.rw);
81*6139Sjb150015 
82*6139Sjb150015 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
83*6139Sjb150015 }
84*6139Sjb150015 
85*6139Sjb150015 smb_sdrc_t
86*6139Sjb150015 smb_com_write(smb_request_t *sr)
87*6139Sjb150015 {
88*6139Sjb150015 	smb_rw_param_t *param = sr->arg.rw;
89*6139Sjb150015 	int rc;
905331Samw 
915331Samw 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
925331Samw 	if (sr->fid_ofile == NULL) {
935772Sas200622 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
94*6139Sjb150015 		return (SDRC_ERROR);
955331Samw 	}
965331Samw 
97*6139Sjb150015 	if (param->rw_count == 0) {
985331Samw 		rc = smb_write_truncate(sr, param);
995331Samw 	} else {
100*6139Sjb150015 		rc = smbsr_decode_data(sr, "D", &param->rw_vdb);
1015331Samw 
102*6139Sjb150015 		if ((rc != 0) || (param->rw_vdb.len != param->rw_count)) {
103*6139Sjb150015 			smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
104*6139Sjb150015 			    ERRDOS, ERROR_INVALID_PARAMETER);
105*6139Sjb150015 			return (SDRC_ERROR);
1065331Samw 		}
1075331Samw 
108*6139Sjb150015 		param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
1095331Samw 
1105331Samw 		rc = smb_write_common(sr, param);
1115331Samw 	}
1125331Samw 
1135331Samw 	if (rc != 0) {
114*6139Sjb150015 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
115*6139Sjb150015 			smbsr_errno(sr, rc);
116*6139Sjb150015 		return (SDRC_ERROR);
1175331Samw 	}
1185331Samw 
119*6139Sjb150015 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1, param->rw_count, 0);
120*6139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
1215331Samw }
1225331Samw 
1235331Samw /*
1245331Samw  * Write count bytes to a file and then close the file.  This function
1255331Samw  * can only be used to write to 32-bit offsets and the client must set
1265331Samw  * WordCount (6 or 12) correctly in order to locate the data to be
1275331Samw  * written.  If an error occurs on the write, the file should still be
1285331Samw  * closed.  If Count is 0, the file is truncated (or extended) to offset.
1295331Samw  *
1305331Samw  * If the last_write time is non-zero, last_write should be used to set
1315331Samw  * the mtime.  Otherwise the file system stamps the mtime.  Failure to
1325331Samw  * set mtime should not result in an error response.
1335331Samw  */
1346030Sjb150015 smb_sdrc_t
135*6139Sjb150015 smb_pre_write_and_close(smb_request_t *sr)
1365331Samw {
137*6139Sjb150015 	smb_rw_param_t *param;
1385331Samw 	uint32_t off;
139*6139Sjb150015 	int rc;
1405331Samw 
141*6139Sjb150015 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
142*6139Sjb150015 	sr->arg.rw = param;
143*6139Sjb150015 	param->rw_magic = SMB_RW_MAGIC;
1445331Samw 
1455331Samw 	if (sr->smb_wct == 12) {
1465331Samw 		rc = smbsr_decode_vwv(sr, "wwll12.", &sr->smb_fid,
147*6139Sjb150015 		    &param->rw_count, &off, &param->rw_last_write);
1485331Samw 	} else {
1495331Samw 		rc = smbsr_decode_vwv(sr, "wwll", &sr->smb_fid,
150*6139Sjb150015 		    &param->rw_count, &off, &param->rw_last_write);
1515331Samw 	}
1525331Samw 
153*6139Sjb150015 	param->rw_offset = (uint64_t)off;
154*6139Sjb150015 
155*6139Sjb150015 	DTRACE_SMB_2(op__WriteAndClose__start, smb_request_t *, sr,
156*6139Sjb150015 	    smb_rw_param_t *, param);
157*6139Sjb150015 
158*6139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
159*6139Sjb150015 }
160*6139Sjb150015 
161*6139Sjb150015 void
162*6139Sjb150015 smb_post_write_and_close(smb_request_t *sr)
163*6139Sjb150015 {
164*6139Sjb150015 	DTRACE_SMB_2(op__WriteAndClose__done, smb_request_t *, sr,
165*6139Sjb150015 	    smb_rw_param_t *, sr->arg.rw);
166*6139Sjb150015 
167*6139Sjb150015 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
168*6139Sjb150015 }
169*6139Sjb150015 
170*6139Sjb150015 smb_sdrc_t
171*6139Sjb150015 smb_com_write_and_close(smb_request_t *sr)
172*6139Sjb150015 {
173*6139Sjb150015 	smb_rw_param_t *param = sr->arg.rw;
174*6139Sjb150015 	int rc = 0;
1755331Samw 
1765331Samw 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
1775331Samw 	if (sr->fid_ofile == NULL) {
1785772Sas200622 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
179*6139Sjb150015 		return (SDRC_ERROR);
1805331Samw 	}
1815331Samw 
182*6139Sjb150015 	if (param->rw_count == 0) {
1835331Samw 		rc = smb_write_truncate(sr, param);
1845331Samw 	} else {
1855331Samw 		/*
1865331Samw 		 * There may be a bug here: should this be "3.#B"?
1875331Samw 		 */
188*6139Sjb150015 		rc = smbsr_decode_data(sr, ".#B", param->rw_count,
189*6139Sjb150015 		    &param->rw_vdb);
1905331Samw 
191*6139Sjb150015 		if ((rc != 0) || (param->rw_vdb.len != param->rw_count)) {
192*6139Sjb150015 			smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
193*6139Sjb150015 			    ERRDOS, ERROR_INVALID_PARAMETER);
194*6139Sjb150015 			return (SDRC_ERROR);
1955331Samw 		}
1965331Samw 
197*6139Sjb150015 		param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
1985331Samw 
1995331Samw 		rc = smb_write_common(sr, param);
2005331Samw 	}
2015331Samw 
2025331Samw 	if (rc != 0) {
203*6139Sjb150015 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
204*6139Sjb150015 			smbsr_errno(sr, rc);
205*6139Sjb150015 		return (SDRC_ERROR);
2065331Samw 	}
2075331Samw 
208*6139Sjb150015 	if ((rc = smb_common_close(sr, param->rw_last_write)) != 0) {
2095772Sas200622 		smbsr_errno(sr, rc);
210*6139Sjb150015 		return (SDRC_ERROR);
2115331Samw 	}
2125331Samw 
213*6139Sjb150015 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1, param->rw_count, 0);
214*6139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
2155331Samw }
2165331Samw 
2175331Samw /*
2185331Samw  * Write count bytes to a file at the specified offset and then unlock
2195331Samw  * them.  Write behind is safe because the client should have the range
2205331Samw  * locked and this request is allowed to extend the file - note that
221*6139Sjb150015  * offset is limited to 32-bits.
222*6139Sjb150015  *
223*6139Sjb150015  * Spec advice: it is an error for count to be zero.  For compatibility,
224*6139Sjb150015  * we take no action and return success.
2255331Samw  *
2265331Samw  * The SmbLockAndRead/SmbWriteAndUnlock sub-dialect is only valid on disk
2275331Samw  * files.  Reject any attempt to use it on other shares.
2285331Samw  *
2295331Samw  * The response count indicates the actual number of bytes written, which
2305331Samw  * will equal the requested count on success.  If request and response
2315331Samw  * counts differ but there is no error, the client will assume that the
2325331Samw  * server encountered a resource issue.
2335331Samw  */
2346030Sjb150015 smb_sdrc_t
235*6139Sjb150015 smb_pre_write_and_unlock(smb_request_t *sr)
2365331Samw {
237*6139Sjb150015 	smb_rw_param_t *param;
2385331Samw 	uint32_t off;
2395331Samw 	uint16_t remcnt;
240*6139Sjb150015 	int rc;
241*6139Sjb150015 
242*6139Sjb150015 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
243*6139Sjb150015 	sr->arg.rw = param;
244*6139Sjb150015 	param->rw_magic = SMB_RW_MAGIC;
245*6139Sjb150015 
246*6139Sjb150015 	rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, &param->rw_count, &off,
247*6139Sjb150015 	    &remcnt);
248*6139Sjb150015 
249*6139Sjb150015 	param->rw_offset = (uint64_t)off;
250*6139Sjb150015 
251*6139Sjb150015 	DTRACE_SMB_2(op__WriteAndUnlock__start, smb_request_t *, sr,
252*6139Sjb150015 	    smb_rw_param_t *, param);
253*6139Sjb150015 
254*6139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
255*6139Sjb150015 }
256*6139Sjb150015 
257*6139Sjb150015 void
258*6139Sjb150015 smb_post_write_and_unlock(smb_request_t *sr)
259*6139Sjb150015 {
260*6139Sjb150015 	DTRACE_SMB_2(op__WriteAndUnlock__done, smb_request_t *, sr,
261*6139Sjb150015 	    smb_rw_param_t *, sr->arg.rw);
262*6139Sjb150015 
263*6139Sjb150015 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
264*6139Sjb150015 }
265*6139Sjb150015 
266*6139Sjb150015 smb_sdrc_t
267*6139Sjb150015 smb_com_write_and_unlock(smb_request_t *sr)
268*6139Sjb150015 {
269*6139Sjb150015 	smb_rw_param_t *param = sr->arg.rw;
270*6139Sjb150015 	uint32_t status;
2715331Samw 	int rc = 0;
2725331Samw 
2735331Samw 	if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
2745772Sas200622 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
275*6139Sjb150015 		return (SDRC_ERROR);
2765331Samw 	}
2775331Samw 
2785331Samw 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
2795331Samw 	if (sr->fid_ofile == NULL) {
2805772Sas200622 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
281*6139Sjb150015 		return (SDRC_ERROR);
2825331Samw 	}
2835331Samw 
284*6139Sjb150015 	if (param->rw_count == 0) {
285*6139Sjb150015 		rc = smbsr_encode_result(sr, 1, 0, "bww", 1, 0, 0);
286*6139Sjb150015 		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
2875331Samw 	}
2885331Samw 
289*6139Sjb150015 	rc = smbsr_decode_data(sr, "D", &param->rw_vdb);
2905331Samw 
291*6139Sjb150015 	if ((rc != 0) || (param->rw_count != param->rw_vdb.len)) {
292*6139Sjb150015 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
293*6139Sjb150015 		    ERRDOS, ERROR_INVALID_PARAMETER);
294*6139Sjb150015 		return (SDRC_ERROR);
2955331Samw 	}
2965331Samw 
297*6139Sjb150015 	param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
2985331Samw 
2995331Samw 	if ((rc = smb_write_common(sr, param)) != 0) {
300*6139Sjb150015 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
301*6139Sjb150015 			smbsr_errno(sr, rc);
302*6139Sjb150015 		return (SDRC_ERROR);
3035331Samw 	}
3045331Samw 
305*6139Sjb150015 	status = smb_unlock_range(sr, sr->fid_ofile->f_node, param->rw_offset,
306*6139Sjb150015 	    (uint64_t)param->rw_count);
307*6139Sjb150015 	if (status != NT_STATUS_SUCCESS) {
3085772Sas200622 		smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
3095772Sas200622 		    ERRDOS, ERRnotlocked);
310*6139Sjb150015 		return (SDRC_ERROR);
3115331Samw 	}
3125331Samw 
313*6139Sjb150015 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1, param->rw_count, 0);
314*6139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
3155331Samw }
3165331Samw 
3175331Samw /*
3185331Samw  * Write bytes to a file (SMB Core).  This request was extended in
3195331Samw  * LM 0.12 to support 64-bit offsets, indicated by sending a wct of
3205331Samw  * 14, instead of 12, and including additional offset information.
3215331Samw  *
3225331Samw  * A ByteCount of 0 does not truncate the file - use SMB_COM_WRITE
3235331Samw  * to truncate a file.  A zero length merely transfers zero bytes.
3245331Samw  *
3255331Samw  * If bit 0 of WriteMode is set, Fid must refer to a disk file and
3265331Samw  * the data must be on stable storage before responding.
3275331Samw  */
3286030Sjb150015 smb_sdrc_t
329*6139Sjb150015 smb_pre_write_andx(smb_request_t *sr)
3305331Samw {
331*6139Sjb150015 	smb_rw_param_t *param;
3325331Samw 	uint32_t off_low;
3335331Samw 	uint32_t off_high;
3345331Samw 	uint16_t remcnt;
335*6139Sjb150015 	int rc;
3365331Samw 
337*6139Sjb150015 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
338*6139Sjb150015 	sr->arg.rw = param;
339*6139Sjb150015 	param->rw_magic = SMB_RW_MAGIC;
3405331Samw 
3415331Samw 	if (sr->smb_wct == 14) {
3425331Samw 		rc = smbsr_decode_vwv(sr, "4.wl4.ww2.wwl", &sr->smb_fid,
343*6139Sjb150015 		    &off_low, &param->rw_mode, &remcnt, &param->rw_count,
344*6139Sjb150015 		    &param->rw_dsoff, &off_high);
3455331Samw 
346*6139Sjb150015 		param->rw_dsoff -= 63;
347*6139Sjb150015 		param->rw_offset = ((uint64_t)off_high << 32) | off_low;
3485331Samw 	} else {
3495331Samw 		rc = smbsr_decode_vwv(sr, "4.wl4.ww2.ww", &sr->smb_fid,
350*6139Sjb150015 		    &off_low, &param->rw_mode, &remcnt, &param->rw_count,
351*6139Sjb150015 		    &param->rw_dsoff);
3525331Samw 
353*6139Sjb150015 		param->rw_offset = (uint64_t)off_low;
354*6139Sjb150015 		param->rw_dsoff -= 59;
3555331Samw 	}
3565331Samw 
357*6139Sjb150015 	DTRACE_SMB_2(op__WriteX__start, smb_request_t *, sr,
358*6139Sjb150015 	    smb_rw_param_t *, param);
359*6139Sjb150015 
360*6139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
361*6139Sjb150015 }
362*6139Sjb150015 
363*6139Sjb150015 void
364*6139Sjb150015 smb_post_write_andx(smb_request_t *sr)
365*6139Sjb150015 {
366*6139Sjb150015 	DTRACE_SMB_2(op__WriteX__done, smb_request_t *, sr,
367*6139Sjb150015 	    smb_rw_param_t *, sr->arg.rw);
368*6139Sjb150015 
369*6139Sjb150015 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
370*6139Sjb150015 }
371*6139Sjb150015 
372*6139Sjb150015 smb_sdrc_t
373*6139Sjb150015 smb_com_write_andx(smb_request_t *sr)
374*6139Sjb150015 {
375*6139Sjb150015 	smb_rw_param_t *param = sr->arg.rw;
376*6139Sjb150015 	int rc;
377*6139Sjb150015 
378*6139Sjb150015 	ASSERT(param);
379*6139Sjb150015 	ASSERT(param->rw_magic == SMB_RW_MAGIC);
3805331Samw 
3815331Samw 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
3825331Samw 	if (sr->fid_ofile == NULL) {
3835772Sas200622 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
384*6139Sjb150015 		return (SDRC_ERROR);
3855331Samw 	}
3865331Samw 
387*6139Sjb150015 	if (SMB_WRMODE_IS_STABLE(param->rw_mode) &&
3885331Samw 	    STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
3895772Sas200622 		smbsr_error(sr, 0, ERRSRV, ERRaccess);
390*6139Sjb150015 		return (SDRC_ERROR);
3915331Samw 	}
3925331Samw 
393*6139Sjb150015 	rc = smbsr_decode_data(sr, "#.#B", param->rw_dsoff, param->rw_count,
394*6139Sjb150015 	    &param->rw_vdb);
395*6139Sjb150015 	if ((rc != 0) || (param->rw_vdb.len != param->rw_count)) {
396*6139Sjb150015 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
397*6139Sjb150015 		    ERRDOS, ERROR_INVALID_PARAMETER);
398*6139Sjb150015 		return (SDRC_ERROR);
3995331Samw 	}
4005331Samw 
401*6139Sjb150015 	param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
4025331Samw 
403*6139Sjb150015 	if (param->rw_count != 0) {
4045331Samw 		if ((rc = smb_write_common(sr, param)) != 0) {
405*6139Sjb150015 			if (sr->smb_error.status !=
406*6139Sjb150015 			    NT_STATUS_FILE_LOCK_CONFLICT)
407*6139Sjb150015 				smbsr_errno(sr, rc);
408*6139Sjb150015 			return (SDRC_ERROR);
4095331Samw 		}
4105331Samw 	}
4115331Samw 
4126030Sjb150015 	rc = smbsr_encode_result(sr, 6, 0, "bb1.ww6.w",
413*6139Sjb150015 	    6, sr->andx_com, 15, param->rw_count, 0);
4145331Samw 
415*6139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
4165331Samw }
4175331Samw 
4185331Samw /*
4195331Samw  * Common function for writing files or IPC/MSRPC named pipes.
4205331Samw  *
4215331Samw  * Returns errno values.
4225331Samw  */
4236030Sjb150015 static int
424*6139Sjb150015 smb_write_common(smb_request_t *sr, smb_rw_param_t *param)
4255331Samw {
4265331Samw 	struct smb_ofile *ofile = sr->fid_ofile;
4275331Samw 	smb_node_t *node;
4285331Samw 	uint32_t stability = FSSTAB_UNSTABLE;
4295331Samw 	uint32_t lcount;
4305331Samw 	int rc = 0;
4315331Samw 
4325331Samw 	switch (sr->tid_tree->t_res_type & STYPE_MASK) {
4335331Samw 	case STYPE_DISKTREE:
4345331Samw 		node = ofile->f_node;
4355331Samw 
4365331Samw 		if (node->attr.sa_vattr.va_type != VDIR) {
437*6139Sjb150015 			rc = smb_lock_range_access(sr, node, param->rw_offset,
438*6139Sjb150015 			    param->rw_count, B_TRUE);
439*6139Sjb150015 			if (rc != NT_STATUS_SUCCESS) {
440*6139Sjb150015 				smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
441*6139Sjb150015 				    ERRDOS, ERROR_LOCK_VIOLATION);
442*6139Sjb150015 				return (EACCES);
443*6139Sjb150015 			}
4445331Samw 		}
4455331Samw 
446*6139Sjb150015 		if (SMB_WRMODE_IS_STABLE(param->rw_mode) ||
4475331Samw 		    (node->flags & NODE_FLAGS_WRITE_THROUGH)) {
4485331Samw 			stability = FSSTAB_FILE_SYNC;
4495331Samw 		}
4505331Samw 
4515331Samw 		rc = smb_fsop_write(sr, sr->user_cr, node,
452*6139Sjb150015 		    &param->rw_vdb.uio, &lcount, &node->attr, &stability);
4535331Samw 
4545331Samw 		if (rc)
4555331Samw 			return (rc);
4565331Samw 
4575331Samw 		node->flags |= NODE_FLAGS_SYNCATIME;
4585331Samw 
4595331Samw 		if (node->flags & NODE_FLAGS_SET_SIZE) {
460*6139Sjb150015 			if ((param->rw_offset + lcount) >= node->n_size) {
4615331Samw 				node->flags &= ~NODE_FLAGS_SET_SIZE;
462*6139Sjb150015 				node->n_size = param->rw_offset + lcount;
4635331Samw 			}
4645331Samw 		}
4655331Samw 
466*6139Sjb150015 		param->rw_count = (uint16_t)lcount;
4675331Samw 		break;
4685331Samw 
4695331Samw 	case STYPE_IPC:
470*6139Sjb150015 		param->rw_count = (uint16_t)param->rw_vdb.uio.uio_resid;
4715331Samw 
472*6139Sjb150015 		if ((rc = smb_rpc_write(sr, &param->rw_vdb.uio)) != 0)
473*6139Sjb150015 			param->rw_count = 0;
4745331Samw 		break;
4755331Samw 
4765331Samw 	default:
4775331Samw 		rc = EACCES;
4785331Samw 		break;
4795331Samw 	}
4805331Samw 
4815331Samw 	if (rc != 0)
4825331Samw 		return (rc);
4835331Samw 
4845331Samw 	mutex_enter(&ofile->f_mutex);
485*6139Sjb150015 	ofile->f_seek_pos = param->rw_offset + param->rw_count;
4865331Samw 	mutex_exit(&ofile->f_mutex);
4875331Samw 	return (rc);
4885331Samw }
4895331Samw 
4905331Samw /*
4915331Samw  * Truncate a disk file to the specified offset.
4925331Samw  * Typically, w_count will be zero here.
4935331Samw  *
4945331Samw  * Returns errno values.
4955331Samw  */
4966030Sjb150015 static int
497*6139Sjb150015 smb_write_truncate(smb_request_t *sr, smb_rw_param_t *param)
4985331Samw {
4995331Samw 	struct smb_ofile *ofile = sr->fid_ofile;
5005331Samw 	smb_node_t *node = ofile->f_node;
5015772Sas200622 	boolean_t append_only = B_FALSE;
5026030Sjb150015 	uint32_t status;
5035331Samw 	int rc;
5045331Samw 
5055331Samw 	if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0)
5065331Samw 		return (0);
5075331Samw 
5086030Sjb150015 	status = smb_ofile_access(sr->fid_ofile, sr->user_cr, FILE_WRITE_DATA);
5096030Sjb150015 	if (status != NT_STATUS_SUCCESS) {
5106030Sjb150015 		status = smb_ofile_access(sr->fid_ofile, sr->user_cr,
5115772Sas200622 		    FILE_APPEND_DATA);
5126030Sjb150015 		if (status != NT_STATUS_SUCCESS)
5136030Sjb150015 			return (EACCES);
5146030Sjb150015 		else
5155772Sas200622 			append_only = B_TRUE;
5165772Sas200622 	}
5175772Sas200622 
5185772Sas200622 	smb_rwx_xenter(&node->n_lock);
5195772Sas200622 
520*6139Sjb150015 	if (append_only && (param->rw_offset < node->n_size)) {
5215772Sas200622 		smb_rwx_xexit(&node->n_lock);
5226030Sjb150015 		return (EACCES);
5235772Sas200622 	}
5245772Sas200622 
5255331Samw 	if (node->attr.sa_vattr.va_type != VDIR) {
526*6139Sjb150015 		status = smb_lock_range_access(sr, node, param->rw_offset,
527*6139Sjb150015 		    param->rw_count, B_TRUE);
5286030Sjb150015 		if (status != NT_STATUS_SUCCESS) {
5295772Sas200622 			smb_rwx_xexit(&node->n_lock);
530*6139Sjb150015 			smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
531*6139Sjb150015 			    ERRDOS, ERROR_LOCK_VIOLATION);
5326030Sjb150015 			return (EACCES);
5335331Samw 		}
5345331Samw 	}
5355331Samw 
5365331Samw 	node->flags |= NODE_FLAGS_SET_SIZE;
537*6139Sjb150015 	node->n_size = param->rw_offset;
5385331Samw 
5395772Sas200622 	smb_rwx_xexit(&node->n_lock);
5405772Sas200622 
5415331Samw 	if ((rc = smb_set_file_size(sr)) != 0)
5425331Samw 		return (rc);
5435331Samw 
5445331Samw 	mutex_enter(&ofile->f_mutex);
545*6139Sjb150015 	ofile->f_seek_pos = param->rw_offset + param->rw_count;
5465331Samw 	mutex_exit(&ofile->f_mutex);
5475331Samw 	return (0);
5485331Samw }
5495331Samw 
5505331Samw /*
5515331Samw  * Set the file size using the value in the node. The file will only be
5525331Samw  * updated if NODE_FLAGS_SET_SIZE is set.  It is safe to pass a null node
5535331Samw  * pointer, we just return success.
5545331Samw  *
5555331Samw  * The node attributes are refreshed here from the file system. So any
5565331Samw  * attributes that are affected by file size changes, i.e. the mtime,
5575331Samw  * will be current.
5585331Samw  *
5595331Samw  * Note that smb_write_andx cannot be used to reduce the file size so,
5605331Samw  * if this is required, smb_write is called with a count of zero and
5615331Samw  * the appropriate file length in offset. The file should be resized
5625331Samw  * to the length specified by the offset.
5635331Samw  *
5645331Samw  * Returns 0 on success. Otherwise returns EACCES.
5655331Samw  */
5665331Samw int
567*6139Sjb150015 smb_set_file_size(smb_request_t *sr)
5685331Samw {
5695331Samw 	struct smb_node *node;
5705331Samw 	smb_attr_t new_attr;
5715331Samw 	uint32_t dosattr;
5725331Samw 
5735331Samw 	if ((node = sr->fid_ofile->f_node) == 0)
5745331Samw 		return (0);
5755331Samw 
5765331Samw 	if ((node->flags & NODE_FLAGS_SET_SIZE) == 0)
5775331Samw 		return (0);
5785331Samw 
5795331Samw 	node->flags &= ~NODE_FLAGS_SET_SIZE;
5805331Samw 
5815331Samw 	dosattr = smb_node_get_dosattr(node);
5825331Samw 
5835331Samw 	if (dosattr & SMB_FA_READONLY) {
5845331Samw 		if (((node->flags & NODE_FLAGS_CREATED) == 0) ||
5855331Samw 		    (sr->session->s_kid != node->n_orig_session_id))
5865331Samw 			return (EACCES);
5875331Samw 	}
5885331Samw 
5895331Samw 	bzero(&new_attr, sizeof (new_attr));
5905331Samw 	new_attr.sa_vattr.va_size = node->n_size;
5915331Samw 	new_attr.sa_mask = SMB_AT_SIZE;
5925331Samw 
5935331Samw 	(void) smb_fsop_setattr(sr, sr->user_cr, node, &new_attr,
5945331Samw 	    &node->attr);
5955331Samw 
5965331Samw 	return (0);
5975331Samw }
598