xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_write.c (revision 6600:4e63bcd27ae9)
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 
396139Sjb150015 static int smb_write_common(smb_request_t *, smb_rw_param_t *);
406139Sjb150015 static int smb_write_truncate(smb_request_t *, smb_rw_param_t *);
415331Samw 
425331Samw 
435331Samw /*
445331Samw  * Write count bytes at the specified offset in a file.  The offset is
455331Samw  * limited to 32-bits.  If the count is zero, the file is truncated to
465331Samw  * the length specified by the offset.
475331Samw  *
485331Samw  * The response count indicates the actual number of bytes written, which
495331Samw  * will equal the requested count on success.  If request and response
505331Samw  * counts differ but there is no error, the client will assume that the
515331Samw  * server encountered a resource issue.
525331Samw  */
536030Sjb150015 smb_sdrc_t
546139Sjb150015 smb_pre_write(smb_request_t *sr)
555331Samw {
566139Sjb150015 	smb_rw_param_t *param;
575331Samw 	uint32_t off;
585331Samw 	int rc;
595331Samw 
606139Sjb150015 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
616139Sjb150015 	sr->arg.rw = param;
626139Sjb150015 	param->rw_magic = SMB_RW_MAGIC;
636139Sjb150015 
646139Sjb150015 	rc = smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, &param->rw_count, &off);
656139Sjb150015 
666139Sjb150015 	param->rw_offset = (uint64_t)off;
676139Sjb150015 	param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
686139Sjb150015 
696139Sjb150015 	DTRACE_SMB_2(op__Write__start, smb_request_t *, sr,
706139Sjb150015 	    smb_rw_param_t *, param);
716139Sjb150015 
726139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
736139Sjb150015 }
745331Samw 
756139Sjb150015 void
766139Sjb150015 smb_post_write(smb_request_t *sr)
776139Sjb150015 {
786139Sjb150015 	DTRACE_SMB_2(op__Write__done, smb_request_t *, sr,
796139Sjb150015 	    smb_rw_param_t *, sr->arg.rw);
806139Sjb150015 
816139Sjb150015 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
826139Sjb150015 }
836139Sjb150015 
846139Sjb150015 smb_sdrc_t
856139Sjb150015 smb_com_write(smb_request_t *sr)
866139Sjb150015 {
876139Sjb150015 	smb_rw_param_t *param = sr->arg.rw;
886139Sjb150015 	int rc;
895331Samw 
905331Samw 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
915331Samw 	if (sr->fid_ofile == NULL) {
925772Sas200622 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
936139Sjb150015 		return (SDRC_ERROR);
945331Samw 	}
955331Samw 
966139Sjb150015 	if (param->rw_count == 0) {
975331Samw 		rc = smb_write_truncate(sr, param);
985331Samw 	} else {
996139Sjb150015 		rc = smbsr_decode_data(sr, "D", &param->rw_vdb);
1005331Samw 
1016139Sjb150015 		if ((rc != 0) || (param->rw_vdb.len != param->rw_count)) {
1026139Sjb150015 			smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
1036139Sjb150015 			    ERRDOS, ERROR_INVALID_PARAMETER);
1046139Sjb150015 			return (SDRC_ERROR);
1055331Samw 		}
1065331Samw 
1076139Sjb150015 		param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
1085331Samw 
1095331Samw 		rc = smb_write_common(sr, param);
1105331Samw 	}
1115331Samw 
1125331Samw 	if (rc != 0) {
1136139Sjb150015 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
1146139Sjb150015 			smbsr_errno(sr, rc);
1156139Sjb150015 		return (SDRC_ERROR);
1165331Samw 	}
1175331Samw 
1186139Sjb150015 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1, param->rw_count, 0);
1196139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
1205331Samw }
1215331Samw 
1225331Samw /*
1235331Samw  * Write count bytes to a file and then close the file.  This function
1245331Samw  * can only be used to write to 32-bit offsets and the client must set
1255331Samw  * WordCount (6 or 12) correctly in order to locate the data to be
1265331Samw  * written.  If an error occurs on the write, the file should still be
1275331Samw  * closed.  If Count is 0, the file is truncated (or extended) to offset.
1285331Samw  *
1295331Samw  * If the last_write time is non-zero, last_write should be used to set
1305331Samw  * the mtime.  Otherwise the file system stamps the mtime.  Failure to
1315331Samw  * set mtime should not result in an error response.
1325331Samw  */
1336030Sjb150015 smb_sdrc_t
1346139Sjb150015 smb_pre_write_and_close(smb_request_t *sr)
1355331Samw {
1366139Sjb150015 	smb_rw_param_t *param;
1375331Samw 	uint32_t off;
1386139Sjb150015 	int rc;
1395331Samw 
1406139Sjb150015 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
1416139Sjb150015 	sr->arg.rw = param;
1426139Sjb150015 	param->rw_magic = SMB_RW_MAGIC;
1435331Samw 
1445331Samw 	if (sr->smb_wct == 12) {
1455331Samw 		rc = smbsr_decode_vwv(sr, "wwll12.", &sr->smb_fid,
1466139Sjb150015 		    &param->rw_count, &off, &param->rw_last_write);
1475331Samw 	} else {
1485331Samw 		rc = smbsr_decode_vwv(sr, "wwll", &sr->smb_fid,
1496139Sjb150015 		    &param->rw_count, &off, &param->rw_last_write);
1505331Samw 	}
1515331Samw 
1526139Sjb150015 	param->rw_offset = (uint64_t)off;
1536139Sjb150015 
1546139Sjb150015 	DTRACE_SMB_2(op__WriteAndClose__start, smb_request_t *, sr,
1556139Sjb150015 	    smb_rw_param_t *, param);
1566139Sjb150015 
1576139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
1586139Sjb150015 }
1596139Sjb150015 
1606139Sjb150015 void
1616139Sjb150015 smb_post_write_and_close(smb_request_t *sr)
1626139Sjb150015 {
1636139Sjb150015 	DTRACE_SMB_2(op__WriteAndClose__done, smb_request_t *, sr,
1646139Sjb150015 	    smb_rw_param_t *, sr->arg.rw);
1656139Sjb150015 
1666139Sjb150015 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
1676139Sjb150015 }
1686139Sjb150015 
1696139Sjb150015 smb_sdrc_t
1706139Sjb150015 smb_com_write_and_close(smb_request_t *sr)
1716139Sjb150015 {
1726139Sjb150015 	smb_rw_param_t *param = sr->arg.rw;
1736139Sjb150015 	int rc = 0;
1745331Samw 
1755331Samw 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
1765331Samw 	if (sr->fid_ofile == NULL) {
1775772Sas200622 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
1786139Sjb150015 		return (SDRC_ERROR);
1795331Samw 	}
1805331Samw 
1816139Sjb150015 	if (param->rw_count == 0) {
1825331Samw 		rc = smb_write_truncate(sr, param);
1835331Samw 	} else {
1845331Samw 		/*
1855331Samw 		 * There may be a bug here: should this be "3.#B"?
1865331Samw 		 */
1876139Sjb150015 		rc = smbsr_decode_data(sr, ".#B", param->rw_count,
1886139Sjb150015 		    &param->rw_vdb);
1895331Samw 
1906139Sjb150015 		if ((rc != 0) || (param->rw_vdb.len != param->rw_count)) {
1916139Sjb150015 			smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
1926139Sjb150015 			    ERRDOS, ERROR_INVALID_PARAMETER);
1936139Sjb150015 			return (SDRC_ERROR);
1945331Samw 		}
1955331Samw 
1966139Sjb150015 		param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
1975331Samw 
1985331Samw 		rc = smb_write_common(sr, param);
1995331Samw 	}
2005331Samw 
2015331Samw 	if (rc != 0) {
2026139Sjb150015 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
2036139Sjb150015 			smbsr_errno(sr, rc);
2046139Sjb150015 		return (SDRC_ERROR);
2055331Samw 	}
2065331Samw 
2076139Sjb150015 	if ((rc = smb_common_close(sr, param->rw_last_write)) != 0) {
2085772Sas200622 		smbsr_errno(sr, rc);
2096139Sjb150015 		return (SDRC_ERROR);
2105331Samw 	}
2115331Samw 
2126139Sjb150015 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1, param->rw_count, 0);
2136139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
2145331Samw }
2155331Samw 
2165331Samw /*
2175331Samw  * Write count bytes to a file at the specified offset and then unlock
2185331Samw  * them.  Write behind is safe because the client should have the range
2195331Samw  * locked and this request is allowed to extend the file - note that
2206139Sjb150015  * offset is limited to 32-bits.
2216139Sjb150015  *
2226139Sjb150015  * Spec advice: it is an error for count to be zero.  For compatibility,
2236139Sjb150015  * we take no action and return success.
2245331Samw  *
2255331Samw  * The SmbLockAndRead/SmbWriteAndUnlock sub-dialect is only valid on disk
2265331Samw  * files.  Reject any attempt to use it on other shares.
2275331Samw  *
2285331Samw  * The response count indicates the actual number of bytes written, which
2295331Samw  * will equal the requested count on success.  If request and response
2305331Samw  * counts differ but there is no error, the client will assume that the
2315331Samw  * server encountered a resource issue.
2325331Samw  */
2336030Sjb150015 smb_sdrc_t
2346139Sjb150015 smb_pre_write_and_unlock(smb_request_t *sr)
2355331Samw {
2366139Sjb150015 	smb_rw_param_t *param;
2375331Samw 	uint32_t off;
2385331Samw 	uint16_t remcnt;
2396139Sjb150015 	int rc;
2406139Sjb150015 
2416139Sjb150015 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
2426139Sjb150015 	sr->arg.rw = param;
2436139Sjb150015 	param->rw_magic = SMB_RW_MAGIC;
2446139Sjb150015 
2456139Sjb150015 	rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, &param->rw_count, &off,
2466139Sjb150015 	    &remcnt);
2476139Sjb150015 
2486139Sjb150015 	param->rw_offset = (uint64_t)off;
2496139Sjb150015 
2506139Sjb150015 	DTRACE_SMB_2(op__WriteAndUnlock__start, smb_request_t *, sr,
2516139Sjb150015 	    smb_rw_param_t *, param);
2526139Sjb150015 
2536139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
2546139Sjb150015 }
2556139Sjb150015 
2566139Sjb150015 void
2576139Sjb150015 smb_post_write_and_unlock(smb_request_t *sr)
2586139Sjb150015 {
2596139Sjb150015 	DTRACE_SMB_2(op__WriteAndUnlock__done, smb_request_t *, sr,
2606139Sjb150015 	    smb_rw_param_t *, sr->arg.rw);
2616139Sjb150015 
2626139Sjb150015 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
2636139Sjb150015 }
2646139Sjb150015 
2656139Sjb150015 smb_sdrc_t
2666139Sjb150015 smb_com_write_and_unlock(smb_request_t *sr)
2676139Sjb150015 {
2686139Sjb150015 	smb_rw_param_t *param = sr->arg.rw;
2696139Sjb150015 	uint32_t status;
2705331Samw 	int rc = 0;
2715331Samw 
2725331Samw 	if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
2735772Sas200622 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
2746139Sjb150015 		return (SDRC_ERROR);
2755331Samw 	}
2765331Samw 
2775331Samw 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
2785331Samw 	if (sr->fid_ofile == NULL) {
2795772Sas200622 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
2806139Sjb150015 		return (SDRC_ERROR);
2815331Samw 	}
2825331Samw 
2836139Sjb150015 	if (param->rw_count == 0) {
2846139Sjb150015 		rc = smbsr_encode_result(sr, 1, 0, "bww", 1, 0, 0);
2856139Sjb150015 		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
2865331Samw 	}
2875331Samw 
2886139Sjb150015 	rc = smbsr_decode_data(sr, "D", &param->rw_vdb);
2895331Samw 
2906139Sjb150015 	if ((rc != 0) || (param->rw_count != param->rw_vdb.len)) {
2916139Sjb150015 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
2926139Sjb150015 		    ERRDOS, ERROR_INVALID_PARAMETER);
2936139Sjb150015 		return (SDRC_ERROR);
2945331Samw 	}
2955331Samw 
2966139Sjb150015 	param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
2975331Samw 
2985331Samw 	if ((rc = smb_write_common(sr, param)) != 0) {
2996139Sjb150015 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
3006139Sjb150015 			smbsr_errno(sr, rc);
3016139Sjb150015 		return (SDRC_ERROR);
3025331Samw 	}
3035331Samw 
3046139Sjb150015 	status = smb_unlock_range(sr, sr->fid_ofile->f_node, param->rw_offset,
3056139Sjb150015 	    (uint64_t)param->rw_count);
3066139Sjb150015 	if (status != NT_STATUS_SUCCESS) {
3075772Sas200622 		smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
3085772Sas200622 		    ERRDOS, ERRnotlocked);
3096139Sjb150015 		return (SDRC_ERROR);
3105331Samw 	}
3115331Samw 
3126139Sjb150015 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1, param->rw_count, 0);
3136139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
3145331Samw }
3155331Samw 
3165331Samw /*
3175331Samw  * Write bytes to a file (SMB Core).  This request was extended in
3185331Samw  * LM 0.12 to support 64-bit offsets, indicated by sending a wct of
3195331Samw  * 14, instead of 12, and including additional offset information.
3205331Samw  *
3215331Samw  * A ByteCount of 0 does not truncate the file - use SMB_COM_WRITE
3225331Samw  * to truncate a file.  A zero length merely transfers zero bytes.
3235331Samw  *
3245331Samw  * If bit 0 of WriteMode is set, Fid must refer to a disk file and
3255331Samw  * the data must be on stable storage before responding.
3265331Samw  */
3276030Sjb150015 smb_sdrc_t
3286139Sjb150015 smb_pre_write_andx(smb_request_t *sr)
3295331Samw {
3306139Sjb150015 	smb_rw_param_t *param;
3315331Samw 	uint32_t off_low;
3325331Samw 	uint32_t off_high;
3335331Samw 	uint16_t remcnt;
3346139Sjb150015 	int rc;
3355331Samw 
3366139Sjb150015 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
3376139Sjb150015 	sr->arg.rw = param;
3386139Sjb150015 	param->rw_magic = SMB_RW_MAGIC;
3395331Samw 
3405331Samw 	if (sr->smb_wct == 14) {
3415331Samw 		rc = smbsr_decode_vwv(sr, "4.wl4.ww2.wwl", &sr->smb_fid,
3426139Sjb150015 		    &off_low, &param->rw_mode, &remcnt, &param->rw_count,
3436139Sjb150015 		    &param->rw_dsoff, &off_high);
3445331Samw 
3456139Sjb150015 		param->rw_dsoff -= 63;
3466139Sjb150015 		param->rw_offset = ((uint64_t)off_high << 32) | off_low;
3475331Samw 	} else {
3485331Samw 		rc = smbsr_decode_vwv(sr, "4.wl4.ww2.ww", &sr->smb_fid,
3496139Sjb150015 		    &off_low, &param->rw_mode, &remcnt, &param->rw_count,
3506139Sjb150015 		    &param->rw_dsoff);
3515331Samw 
3526139Sjb150015 		param->rw_offset = (uint64_t)off_low;
3536139Sjb150015 		param->rw_dsoff -= 59;
3545331Samw 	}
3555331Samw 
3566139Sjb150015 	DTRACE_SMB_2(op__WriteX__start, smb_request_t *, sr,
3576139Sjb150015 	    smb_rw_param_t *, param);
3586139Sjb150015 
3596139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
3606139Sjb150015 }
3616139Sjb150015 
3626139Sjb150015 void
3636139Sjb150015 smb_post_write_andx(smb_request_t *sr)
3646139Sjb150015 {
3656139Sjb150015 	DTRACE_SMB_2(op__WriteX__done, smb_request_t *, sr,
3666139Sjb150015 	    smb_rw_param_t *, sr->arg.rw);
3676139Sjb150015 
3686139Sjb150015 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
3696139Sjb150015 }
3706139Sjb150015 
3716139Sjb150015 smb_sdrc_t
3726139Sjb150015 smb_com_write_andx(smb_request_t *sr)
3736139Sjb150015 {
3746139Sjb150015 	smb_rw_param_t *param = sr->arg.rw;
3756139Sjb150015 	int rc;
3766139Sjb150015 
3776139Sjb150015 	ASSERT(param);
3786139Sjb150015 	ASSERT(param->rw_magic == SMB_RW_MAGIC);
3795331Samw 
3805331Samw 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
3815331Samw 	if (sr->fid_ofile == NULL) {
3825772Sas200622 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
3836139Sjb150015 		return (SDRC_ERROR);
3845331Samw 	}
3855331Samw 
3866139Sjb150015 	if (SMB_WRMODE_IS_STABLE(param->rw_mode) &&
3875331Samw 	    STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
3885772Sas200622 		smbsr_error(sr, 0, ERRSRV, ERRaccess);
3896139Sjb150015 		return (SDRC_ERROR);
3905331Samw 	}
3915331Samw 
3926139Sjb150015 	rc = smbsr_decode_data(sr, "#.#B", param->rw_dsoff, param->rw_count,
3936139Sjb150015 	    &param->rw_vdb);
3946139Sjb150015 	if ((rc != 0) || (param->rw_vdb.len != param->rw_count)) {
3956139Sjb150015 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
3966139Sjb150015 		    ERRDOS, ERROR_INVALID_PARAMETER);
3976139Sjb150015 		return (SDRC_ERROR);
3985331Samw 	}
3995331Samw 
4006139Sjb150015 	param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
4015331Samw 
4026139Sjb150015 	if (param->rw_count != 0) {
4035331Samw 		if ((rc = smb_write_common(sr, param)) != 0) {
4046139Sjb150015 			if (sr->smb_error.status !=
4056139Sjb150015 			    NT_STATUS_FILE_LOCK_CONFLICT)
4066139Sjb150015 				smbsr_errno(sr, rc);
4076139Sjb150015 			return (SDRC_ERROR);
4085331Samw 		}
4095331Samw 	}
4105331Samw 
4116030Sjb150015 	rc = smbsr_encode_result(sr, 6, 0, "bb1.ww6.w",
4126139Sjb150015 	    6, sr->andx_com, 15, param->rw_count, 0);
4135331Samw 
4146139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
4155331Samw }
4165331Samw 
4175331Samw /*
4185331Samw  * Common function for writing files or IPC/MSRPC named pipes.
4195331Samw  *
4205331Samw  * Returns errno values.
4215331Samw  */
4226030Sjb150015 static int
4236139Sjb150015 smb_write_common(smb_request_t *sr, smb_rw_param_t *param)
4245331Samw {
4255331Samw 	struct smb_ofile *ofile = sr->fid_ofile;
4265331Samw 	smb_node_t *node;
4275331Samw 	uint32_t stability = FSSTAB_UNSTABLE;
4285331Samw 	uint32_t lcount;
4295331Samw 	int rc = 0;
4305331Samw 
4315331Samw 	switch (sr->tid_tree->t_res_type & STYPE_MASK) {
4325331Samw 	case STYPE_DISKTREE:
4335331Samw 		node = ofile->f_node;
4345331Samw 
4355331Samw 		if (node->attr.sa_vattr.va_type != VDIR) {
4366139Sjb150015 			rc = smb_lock_range_access(sr, node, param->rw_offset,
4376139Sjb150015 			    param->rw_count, B_TRUE);
4386139Sjb150015 			if (rc != NT_STATUS_SUCCESS) {
4396139Sjb150015 				smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
4406139Sjb150015 				    ERRDOS, ERROR_LOCK_VIOLATION);
4416139Sjb150015 				return (EACCES);
4426139Sjb150015 			}
4435331Samw 		}
4445331Samw 
4456139Sjb150015 		if (SMB_WRMODE_IS_STABLE(param->rw_mode) ||
4465331Samw 		    (node->flags & NODE_FLAGS_WRITE_THROUGH)) {
4475331Samw 			stability = FSSTAB_FILE_SYNC;
4485331Samw 		}
4495331Samw 
4505331Samw 		rc = smb_fsop_write(sr, sr->user_cr, node,
4516139Sjb150015 		    &param->rw_vdb.uio, &lcount, &node->attr, &stability);
4525331Samw 
4535331Samw 		if (rc)
4545331Samw 			return (rc);
4555331Samw 
4565331Samw 		node->flags |= NODE_FLAGS_SYNCATIME;
4575331Samw 
4585331Samw 		if (node->flags & NODE_FLAGS_SET_SIZE) {
4596139Sjb150015 			if ((param->rw_offset + lcount) >= node->n_size) {
4605331Samw 				node->flags &= ~NODE_FLAGS_SET_SIZE;
4616139Sjb150015 				node->n_size = param->rw_offset + lcount;
4625331Samw 			}
4635331Samw 		}
4645331Samw 
4656139Sjb150015 		param->rw_count = (uint16_t)lcount;
4665331Samw 		break;
4675331Samw 
4685331Samw 	case STYPE_IPC:
4696139Sjb150015 		param->rw_count = (uint16_t)param->rw_vdb.uio.uio_resid;
4705331Samw 
4716139Sjb150015 		if ((rc = smb_rpc_write(sr, &param->rw_vdb.uio)) != 0)
4726139Sjb150015 			param->rw_count = 0;
4735331Samw 		break;
4745331Samw 
4755331Samw 	default:
4765331Samw 		rc = EACCES;
4775331Samw 		break;
4785331Samw 	}
4795331Samw 
4805331Samw 	if (rc != 0)
4815331Samw 		return (rc);
4825331Samw 
4835331Samw 	mutex_enter(&ofile->f_mutex);
4846139Sjb150015 	ofile->f_seek_pos = param->rw_offset + param->rw_count;
4855331Samw 	mutex_exit(&ofile->f_mutex);
4865331Samw 	return (rc);
4875331Samw }
4885331Samw 
4895331Samw /*
4905331Samw  * Truncate a disk file to the specified offset.
4915331Samw  * Typically, w_count will be zero here.
4925331Samw  *
4935331Samw  * Returns errno values.
4945331Samw  */
4956030Sjb150015 static int
4966139Sjb150015 smb_write_truncate(smb_request_t *sr, smb_rw_param_t *param)
4975331Samw {
4985331Samw 	struct smb_ofile *ofile = sr->fid_ofile;
4995331Samw 	smb_node_t *node = ofile->f_node;
5005772Sas200622 	boolean_t append_only = B_FALSE;
5016030Sjb150015 	uint32_t status;
5025331Samw 	int rc;
5035331Samw 
5045331Samw 	if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0)
5055331Samw 		return (0);
5065331Samw 
5076030Sjb150015 	status = smb_ofile_access(sr->fid_ofile, sr->user_cr, FILE_WRITE_DATA);
5086030Sjb150015 	if (status != NT_STATUS_SUCCESS) {
5096030Sjb150015 		status = smb_ofile_access(sr->fid_ofile, sr->user_cr,
5105772Sas200622 		    FILE_APPEND_DATA);
5116030Sjb150015 		if (status != NT_STATUS_SUCCESS)
5126030Sjb150015 			return (EACCES);
5136030Sjb150015 		else
5145772Sas200622 			append_only = B_TRUE;
5155772Sas200622 	}
5165772Sas200622 
5175772Sas200622 	smb_rwx_xenter(&node->n_lock);
5185772Sas200622 
5196139Sjb150015 	if (append_only && (param->rw_offset < node->n_size)) {
5205772Sas200622 		smb_rwx_xexit(&node->n_lock);
5216030Sjb150015 		return (EACCES);
5225772Sas200622 	}
5235772Sas200622 
5245331Samw 	if (node->attr.sa_vattr.va_type != VDIR) {
5256139Sjb150015 		status = smb_lock_range_access(sr, node, param->rw_offset,
5266139Sjb150015 		    param->rw_count, B_TRUE);
5276030Sjb150015 		if (status != NT_STATUS_SUCCESS) {
5285772Sas200622 			smb_rwx_xexit(&node->n_lock);
5296139Sjb150015 			smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
5306139Sjb150015 			    ERRDOS, ERROR_LOCK_VIOLATION);
5316030Sjb150015 			return (EACCES);
5325331Samw 		}
5335331Samw 	}
5345331Samw 
5355331Samw 	node->flags |= NODE_FLAGS_SET_SIZE;
5366139Sjb150015 	node->n_size = param->rw_offset;
5375331Samw 
5385772Sas200622 	smb_rwx_xexit(&node->n_lock);
5395772Sas200622 
540*6600Sas200622 	if ((rc = smb_set_file_size(sr, node)) != 0)
5415331Samw 		return (rc);
5425331Samw 
5435331Samw 	mutex_enter(&ofile->f_mutex);
5446139Sjb150015 	ofile->f_seek_pos = param->rw_offset + param->rw_count;
5455331Samw 	mutex_exit(&ofile->f_mutex);
5465331Samw 	return (0);
5475331Samw }
5485331Samw 
5495331Samw /*
5505331Samw  * Set the file size using the value in the node. The file will only be
5515331Samw  * updated if NODE_FLAGS_SET_SIZE is set.  It is safe to pass a null node
5525331Samw  * pointer, we just return success.
5535331Samw  *
5545331Samw  * The node attributes are refreshed here from the file system. So any
5555331Samw  * attributes that are affected by file size changes, i.e. the mtime,
5565331Samw  * will be current.
5575331Samw  *
5585331Samw  * Note that smb_write_andx cannot be used to reduce the file size so,
5595331Samw  * if this is required, smb_write is called with a count of zero and
5605331Samw  * the appropriate file length in offset. The file should be resized
5615331Samw  * to the length specified by the offset.
5625331Samw  *
5635331Samw  * Returns 0 on success. Otherwise returns EACCES.
5645331Samw  */
5655331Samw int
566*6600Sas200622 smb_set_file_size(smb_request_t *sr, smb_node_t *node)
5675331Samw {
5685331Samw 	smb_attr_t new_attr;
5695331Samw 	uint32_t dosattr;
5705331Samw 
571*6600Sas200622 	if (node == NULL)
5725331Samw 		return (0);
5735331Samw 
5745331Samw 	if ((node->flags & NODE_FLAGS_SET_SIZE) == 0)
5755331Samw 		return (0);
5765331Samw 
5775331Samw 	node->flags &= ~NODE_FLAGS_SET_SIZE;
5785331Samw 
5795331Samw 	dosattr = smb_node_get_dosattr(node);
5805331Samw 
5815331Samw 	if (dosattr & SMB_FA_READONLY) {
5825331Samw 		if (((node->flags & NODE_FLAGS_CREATED) == 0) ||
5835331Samw 		    (sr->session->s_kid != node->n_orig_session_id))
5845331Samw 			return (EACCES);
5855331Samw 	}
5865331Samw 
5875331Samw 	bzero(&new_attr, sizeof (new_attr));
5885331Samw 	new_attr.sa_vattr.va_size = node->n_size;
5895331Samw 	new_attr.sa_mask = SMB_AT_SIZE;
5905331Samw 
5915331Samw 	(void) smb_fsop_setattr(sr, sr->user_cr, node, &new_attr,
5925331Samw 	    &node->attr);
5935331Samw 
5945331Samw 	return (0);
5955331Samw }
596