xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_write.c (revision 8934:8ff6afa44187)
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 /*
22*8934SJose.Borrego@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw 
265331Samw #include <sys/sdt.h>
275331Samw #include <smbsrv/smb_incl.h>
285331Samw #include <smbsrv/smb_fsops.h>
295331Samw #include <smbsrv/mbuf.h>
305331Samw #include <smbsrv/netbios.h>
315331Samw 
325331Samw 
335331Samw #define	SMB_WRMODE_WRITE_THRU	0x0001
345331Samw #define	SMB_WRMODE_IS_STABLE(M)	((M) & SMB_WRMODE_WRITE_THRU)
355331Samw 
36*8934SJose.Borrego@Sun.COM /*
37*8934SJose.Borrego@Sun.COM  * The limit in bytes that the marshalling will grow the buffer
38*8934SJose.Borrego@Sun.COM  * chain to accomodate incoming data on SmbWriteX requests.
39*8934SJose.Borrego@Sun.COM  * This sets the upper limit for the data-count per SmbWriteX
40*8934SJose.Borrego@Sun.COM  * request.
41*8934SJose.Borrego@Sun.COM  */
42*8934SJose.Borrego@Sun.COM #define	SMB_WRITEX_MAX		102400
435331Samw 
446139Sjb150015 static int smb_write_common(smb_request_t *, smb_rw_param_t *);
456139Sjb150015 static int smb_write_truncate(smb_request_t *, smb_rw_param_t *);
465331Samw 
475331Samw 
485331Samw /*
495331Samw  * Write count bytes at the specified offset in a file.  The offset is
505331Samw  * limited to 32-bits.  If the count is zero, the file is truncated to
515331Samw  * the length specified by the offset.
525331Samw  *
535331Samw  * The response count indicates the actual number of bytes written, which
545331Samw  * will equal the requested count on success.  If request and response
555331Samw  * counts differ but there is no error, the client will assume that the
565331Samw  * server encountered a resource issue.
575331Samw  */
586030Sjb150015 smb_sdrc_t
596139Sjb150015 smb_pre_write(smb_request_t *sr)
605331Samw {
616139Sjb150015 	smb_rw_param_t *param;
625331Samw 	uint32_t off;
63*8934SJose.Borrego@Sun.COM 	uint16_t count;
645331Samw 	int rc;
655331Samw 
666139Sjb150015 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
676139Sjb150015 	sr->arg.rw = param;
686139Sjb150015 	param->rw_magic = SMB_RW_MAGIC;
696139Sjb150015 
70*8934SJose.Borrego@Sun.COM 	rc = smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, &count, &off);
716139Sjb150015 
72*8934SJose.Borrego@Sun.COM 	param->rw_count = (uint32_t)count;
736139Sjb150015 	param->rw_offset = (uint64_t)off;
74*8934SJose.Borrego@Sun.COM 	param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
756139Sjb150015 
766139Sjb150015 	DTRACE_SMB_2(op__Write__start, smb_request_t *, sr,
776139Sjb150015 	    smb_rw_param_t *, param);
786139Sjb150015 
796139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
806139Sjb150015 }
815331Samw 
826139Sjb150015 void
836139Sjb150015 smb_post_write(smb_request_t *sr)
846139Sjb150015 {
856139Sjb150015 	DTRACE_SMB_2(op__Write__done, smb_request_t *, sr,
866139Sjb150015 	    smb_rw_param_t *, sr->arg.rw);
876139Sjb150015 
886139Sjb150015 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
896139Sjb150015 }
906139Sjb150015 
916139Sjb150015 smb_sdrc_t
926139Sjb150015 smb_com_write(smb_request_t *sr)
936139Sjb150015 {
946139Sjb150015 	smb_rw_param_t *param = sr->arg.rw;
956139Sjb150015 	int rc;
965331Samw 
97*8934SJose.Borrego@Sun.COM 	smbsr_lookup_file(sr);
985331Samw 	if (sr->fid_ofile == NULL) {
995772Sas200622 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
1006139Sjb150015 		return (SDRC_ERROR);
1015331Samw 	}
1025331Samw 
1037961SNatalie.Li@Sun.COM 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
1047961SNatalie.Li@Sun.COM 
1056139Sjb150015 	if (param->rw_count == 0) {
1065331Samw 		rc = smb_write_truncate(sr, param);
1075331Samw 	} else {
1086139Sjb150015 		rc = smbsr_decode_data(sr, "D", &param->rw_vdb);
1095331Samw 
110*8934SJose.Borrego@Sun.COM 		if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
1116139Sjb150015 			smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
1126139Sjb150015 			    ERRDOS, ERROR_INVALID_PARAMETER);
1136139Sjb150015 			return (SDRC_ERROR);
1145331Samw 		}
1155331Samw 
116*8934SJose.Borrego@Sun.COM 		param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
1175331Samw 
1185331Samw 		rc = smb_write_common(sr, param);
1195331Samw 	}
1205331Samw 
1215331Samw 	if (rc != 0) {
1226139Sjb150015 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
1236139Sjb150015 			smbsr_errno(sr, rc);
1246139Sjb150015 		return (SDRC_ERROR);
1255331Samw 	}
1265331Samw 
127*8934SJose.Borrego@Sun.COM 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
128*8934SJose.Borrego@Sun.COM 	    (uint16_t)param->rw_count, 0);
1296139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
1305331Samw }
1315331Samw 
1325331Samw /*
1335331Samw  * Write count bytes to a file and then close the file.  This function
1345331Samw  * can only be used to write to 32-bit offsets and the client must set
1355331Samw  * WordCount (6 or 12) correctly in order to locate the data to be
1365331Samw  * written.  If an error occurs on the write, the file should still be
1375331Samw  * closed.  If Count is 0, the file is truncated (or extended) to offset.
1385331Samw  *
1395331Samw  * If the last_write time is non-zero, last_write should be used to set
1405331Samw  * the mtime.  Otherwise the file system stamps the mtime.  Failure to
1415331Samw  * set mtime should not result in an error response.
1425331Samw  */
1436030Sjb150015 smb_sdrc_t
1446139Sjb150015 smb_pre_write_and_close(smb_request_t *sr)
1455331Samw {
1466139Sjb150015 	smb_rw_param_t *param;
1475331Samw 	uint32_t off;
148*8934SJose.Borrego@Sun.COM 	uint16_t count;
1496139Sjb150015 	int rc;
1505331Samw 
1516139Sjb150015 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
1526139Sjb150015 	sr->arg.rw = param;
1536139Sjb150015 	param->rw_magic = SMB_RW_MAGIC;
1545331Samw 
1555331Samw 	if (sr->smb_wct == 12) {
1565331Samw 		rc = smbsr_decode_vwv(sr, "wwll12.", &sr->smb_fid,
157*8934SJose.Borrego@Sun.COM 		    &count, &off, &param->rw_last_write);
1585331Samw 	} else {
1595331Samw 		rc = smbsr_decode_vwv(sr, "wwll", &sr->smb_fid,
160*8934SJose.Borrego@Sun.COM 		    &count, &off, &param->rw_last_write);
1615331Samw 	}
1625331Samw 
163*8934SJose.Borrego@Sun.COM 	param->rw_count = (uint32_t)count;
1646139Sjb150015 	param->rw_offset = (uint64_t)off;
1656139Sjb150015 
1666139Sjb150015 	DTRACE_SMB_2(op__WriteAndClose__start, smb_request_t *, sr,
1676139Sjb150015 	    smb_rw_param_t *, param);
1686139Sjb150015 
1696139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
1706139Sjb150015 }
1716139Sjb150015 
1726139Sjb150015 void
1736139Sjb150015 smb_post_write_and_close(smb_request_t *sr)
1746139Sjb150015 {
1756139Sjb150015 	DTRACE_SMB_2(op__WriteAndClose__done, smb_request_t *, sr,
1766139Sjb150015 	    smb_rw_param_t *, sr->arg.rw);
1776139Sjb150015 
1786139Sjb150015 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
1796139Sjb150015 }
1806139Sjb150015 
1816139Sjb150015 smb_sdrc_t
1826139Sjb150015 smb_com_write_and_close(smb_request_t *sr)
1836139Sjb150015 {
1846139Sjb150015 	smb_rw_param_t *param = sr->arg.rw;
185*8934SJose.Borrego@Sun.COM 	uint16_t count;
1866139Sjb150015 	int rc = 0;
1875331Samw 
188*8934SJose.Borrego@Sun.COM 	smbsr_lookup_file(sr);
1895331Samw 	if (sr->fid_ofile == NULL) {
1905772Sas200622 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
1916139Sjb150015 		return (SDRC_ERROR);
1925331Samw 	}
1935331Samw 
1947961SNatalie.Li@Sun.COM 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
1957961SNatalie.Li@Sun.COM 
1966139Sjb150015 	if (param->rw_count == 0) {
1975331Samw 		rc = smb_write_truncate(sr, param);
1985331Samw 	} else {
1995331Samw 		/*
2005331Samw 		 * There may be a bug here: should this be "3.#B"?
2015331Samw 		 */
2026139Sjb150015 		rc = smbsr_decode_data(sr, ".#B", param->rw_count,
2036139Sjb150015 		    &param->rw_vdb);
2045331Samw 
205*8934SJose.Borrego@Sun.COM 		if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
2066139Sjb150015 			smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
2076139Sjb150015 			    ERRDOS, ERROR_INVALID_PARAMETER);
2086139Sjb150015 			return (SDRC_ERROR);
2095331Samw 		}
2105331Samw 
211*8934SJose.Borrego@Sun.COM 		param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
2125331Samw 
2135331Samw 		rc = smb_write_common(sr, param);
2145331Samw 	}
2155331Samw 
2165331Samw 	if (rc != 0) {
2176139Sjb150015 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
2186139Sjb150015 			smbsr_errno(sr, rc);
2196139Sjb150015 		return (SDRC_ERROR);
2205331Samw 	}
2215331Samw 
2227348SJose.Borrego@Sun.COM 	smb_ofile_close(sr->fid_ofile, param->rw_last_write);
2235331Samw 
224*8934SJose.Borrego@Sun.COM 	count = (uint16_t)param->rw_count;
225*8934SJose.Borrego@Sun.COM 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1, count, 0);
2266139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
2275331Samw }
2285331Samw 
2295331Samw /*
2305331Samw  * Write count bytes to a file at the specified offset and then unlock
2315331Samw  * them.  Write behind is safe because the client should have the range
2325331Samw  * locked and this request is allowed to extend the file - note that
2336139Sjb150015  * offset is limited to 32-bits.
2346139Sjb150015  *
2356139Sjb150015  * Spec advice: it is an error for count to be zero.  For compatibility,
2366139Sjb150015  * we take no action and return success.
2375331Samw  *
2385331Samw  * The SmbLockAndRead/SmbWriteAndUnlock sub-dialect is only valid on disk
2395331Samw  * files.  Reject any attempt to use it on other shares.
2405331Samw  *
2415331Samw  * The response count indicates the actual number of bytes written, which
2425331Samw  * will equal the requested count on success.  If request and response
2435331Samw  * counts differ but there is no error, the client will assume that the
2445331Samw  * server encountered a resource issue.
2455331Samw  */
2466030Sjb150015 smb_sdrc_t
2476139Sjb150015 smb_pre_write_and_unlock(smb_request_t *sr)
2485331Samw {
2496139Sjb150015 	smb_rw_param_t *param;
2505331Samw 	uint32_t off;
251*8934SJose.Borrego@Sun.COM 	uint16_t count;
2525331Samw 	uint16_t remcnt;
2536139Sjb150015 	int rc;
2546139Sjb150015 
2556139Sjb150015 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
2566139Sjb150015 	sr->arg.rw = param;
2576139Sjb150015 	param->rw_magic = SMB_RW_MAGIC;
2586139Sjb150015 
259*8934SJose.Borrego@Sun.COM 	rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, &count, &off, &remcnt);
2606139Sjb150015 
261*8934SJose.Borrego@Sun.COM 	param->rw_count = (uint32_t)count;
2626139Sjb150015 	param->rw_offset = (uint64_t)off;
2636139Sjb150015 
2646139Sjb150015 	DTRACE_SMB_2(op__WriteAndUnlock__start, smb_request_t *, sr,
2656139Sjb150015 	    smb_rw_param_t *, param);
2666139Sjb150015 
2676139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
2686139Sjb150015 }
2696139Sjb150015 
2706139Sjb150015 void
2716139Sjb150015 smb_post_write_and_unlock(smb_request_t *sr)
2726139Sjb150015 {
2736139Sjb150015 	DTRACE_SMB_2(op__WriteAndUnlock__done, smb_request_t *, sr,
2746139Sjb150015 	    smb_rw_param_t *, sr->arg.rw);
2756139Sjb150015 
2766139Sjb150015 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
2776139Sjb150015 }
2786139Sjb150015 
2796139Sjb150015 smb_sdrc_t
2806139Sjb150015 smb_com_write_and_unlock(smb_request_t *sr)
2816139Sjb150015 {
2826139Sjb150015 	smb_rw_param_t *param = sr->arg.rw;
2836139Sjb150015 	uint32_t status;
2845331Samw 	int rc = 0;
2855331Samw 
2865331Samw 	if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
2875772Sas200622 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
2886139Sjb150015 		return (SDRC_ERROR);
2895331Samw 	}
2905331Samw 
291*8934SJose.Borrego@Sun.COM 	smbsr_lookup_file(sr);
2925331Samw 	if (sr->fid_ofile == NULL) {
2935772Sas200622 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
2946139Sjb150015 		return (SDRC_ERROR);
2955331Samw 	}
2965331Samw 
2977961SNatalie.Li@Sun.COM 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
2987961SNatalie.Li@Sun.COM 
2996139Sjb150015 	if (param->rw_count == 0) {
3006139Sjb150015 		rc = smbsr_encode_result(sr, 1, 0, "bww", 1, 0, 0);
3016139Sjb150015 		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
3025331Samw 	}
3035331Samw 
304*8934SJose.Borrego@Sun.COM 
3056139Sjb150015 	rc = smbsr_decode_data(sr, "D", &param->rw_vdb);
3065331Samw 
307*8934SJose.Borrego@Sun.COM 	if ((rc != 0) || (param->rw_count != param->rw_vdb.vdb_len)) {
3086139Sjb150015 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
3096139Sjb150015 		    ERRDOS, ERROR_INVALID_PARAMETER);
3106139Sjb150015 		return (SDRC_ERROR);
3115331Samw 	}
3125331Samw 
313*8934SJose.Borrego@Sun.COM 	param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
3145331Samw 
3155331Samw 	if ((rc = smb_write_common(sr, param)) != 0) {
3166139Sjb150015 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
3176139Sjb150015 			smbsr_errno(sr, rc);
3186139Sjb150015 		return (SDRC_ERROR);
3195331Samw 	}
3205331Samw 
3216139Sjb150015 	status = smb_unlock_range(sr, sr->fid_ofile->f_node, param->rw_offset,
3226139Sjb150015 	    (uint64_t)param->rw_count);
3236139Sjb150015 	if (status != NT_STATUS_SUCCESS) {
3245772Sas200622 		smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
3255772Sas200622 		    ERRDOS, ERRnotlocked);
3266139Sjb150015 		return (SDRC_ERROR);
3275331Samw 	}
3285331Samw 
329*8934SJose.Borrego@Sun.COM 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
330*8934SJose.Borrego@Sun.COM 	    (uint16_t)param->rw_count, 0);
3316139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
3325331Samw }
3335331Samw 
3345331Samw /*
3355331Samw  * Write bytes to a file (SMB Core).  This request was extended in
3365331Samw  * LM 0.12 to support 64-bit offsets, indicated by sending a wct of
3375331Samw  * 14, instead of 12, and including additional offset information.
3385331Samw  *
3395331Samw  * A ByteCount of 0 does not truncate the file - use SMB_COM_WRITE
3405331Samw  * to truncate a file.  A zero length merely transfers zero bytes.
3415331Samw  *
3425331Samw  * If bit 0 of WriteMode is set, Fid must refer to a disk file and
3435331Samw  * the data must be on stable storage before responding.
344*8934SJose.Borrego@Sun.COM  *
345*8934SJose.Borrego@Sun.COM  * MS-SMB 3.3.5.8 update to LM 0.12 4.2.5:
346*8934SJose.Borrego@Sun.COM  * If CAP_LARGE_WRITEX is set, the byte count may be larger than the
347*8934SJose.Borrego@Sun.COM  * negotiated buffer size and the server is expected to write the
348*8934SJose.Borrego@Sun.COM  * number of bytes specified.
3495331Samw  */
3506030Sjb150015 smb_sdrc_t
3516139Sjb150015 smb_pre_write_andx(smb_request_t *sr)
3525331Samw {
3536139Sjb150015 	smb_rw_param_t *param;
3545331Samw 	uint32_t off_low;
3555331Samw 	uint32_t off_high;
356*8934SJose.Borrego@Sun.COM 	uint16_t datalen_low;
357*8934SJose.Borrego@Sun.COM 	uint16_t datalen_high;
3585331Samw 	uint16_t remcnt;
3596139Sjb150015 	int rc;
3605331Samw 
3616139Sjb150015 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
3626139Sjb150015 	sr->arg.rw = param;
3636139Sjb150015 	param->rw_magic = SMB_RW_MAGIC;
3645331Samw 
3655331Samw 	if (sr->smb_wct == 14) {
366*8934SJose.Borrego@Sun.COM 		rc = smbsr_decode_vwv(sr, "4.wl4.wwwwwl", &sr->smb_fid,
367*8934SJose.Borrego@Sun.COM 		    &off_low, &param->rw_mode, &remcnt, &datalen_high,
368*8934SJose.Borrego@Sun.COM 		    &datalen_low, &param->rw_dsoff, &off_high);
3695331Samw 
3706139Sjb150015 		param->rw_dsoff -= 63;
3716139Sjb150015 		param->rw_offset = ((uint64_t)off_high << 32) | off_low;
3725331Samw 	} else {
373*8934SJose.Borrego@Sun.COM 		rc = smbsr_decode_vwv(sr, "4.wl4.wwwww", &sr->smb_fid,
374*8934SJose.Borrego@Sun.COM 		    &off_low, &param->rw_mode, &remcnt, &datalen_high,
375*8934SJose.Borrego@Sun.COM 		    &datalen_low, &param->rw_dsoff);
3765331Samw 
3776139Sjb150015 		param->rw_offset = (uint64_t)off_low;
3786139Sjb150015 		param->rw_dsoff -= 59;
3795331Samw 	}
3805331Samw 
381*8934SJose.Borrego@Sun.COM 	param->rw_count = ((uint32_t)datalen_high << 16) |
382*8934SJose.Borrego@Sun.COM 	    (uint32_t)datalen_low;
383*8934SJose.Borrego@Sun.COM 
3846139Sjb150015 	DTRACE_SMB_2(op__WriteX__start, smb_request_t *, sr,
3856139Sjb150015 	    smb_rw_param_t *, param);
3866139Sjb150015 
3876139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
3886139Sjb150015 }
3896139Sjb150015 
3906139Sjb150015 void
3916139Sjb150015 smb_post_write_andx(smb_request_t *sr)
3926139Sjb150015 {
3936139Sjb150015 	DTRACE_SMB_2(op__WriteX__done, smb_request_t *, sr,
3946139Sjb150015 	    smb_rw_param_t *, sr->arg.rw);
3956139Sjb150015 
3966139Sjb150015 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
3976139Sjb150015 }
3986139Sjb150015 
3996139Sjb150015 smb_sdrc_t
4006139Sjb150015 smb_com_write_andx(smb_request_t *sr)
4016139Sjb150015 {
4026139Sjb150015 	smb_rw_param_t *param = sr->arg.rw;
403*8934SJose.Borrego@Sun.COM 	uint16_t count_high;
404*8934SJose.Borrego@Sun.COM 	uint16_t count_low;
4056139Sjb150015 	int rc;
4066139Sjb150015 
4076139Sjb150015 	ASSERT(param);
4086139Sjb150015 	ASSERT(param->rw_magic == SMB_RW_MAGIC);
4095331Samw 
410*8934SJose.Borrego@Sun.COM 	smbsr_lookup_file(sr);
4115331Samw 	if (sr->fid_ofile == NULL) {
4125772Sas200622 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
4136139Sjb150015 		return (SDRC_ERROR);
4145331Samw 	}
4155331Samw 
4167961SNatalie.Li@Sun.COM 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
4177961SNatalie.Li@Sun.COM 
4186139Sjb150015 	if (SMB_WRMODE_IS_STABLE(param->rw_mode) &&
4195331Samw 	    STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
4205772Sas200622 		smbsr_error(sr, 0, ERRSRV, ERRaccess);
4216139Sjb150015 		return (SDRC_ERROR);
4225331Samw 	}
4235331Samw 
424*8934SJose.Borrego@Sun.COM 	sr->smb_data.max_bytes = SMB_WRITEX_MAX;
4256139Sjb150015 	rc = smbsr_decode_data(sr, "#.#B", param->rw_dsoff, param->rw_count,
4266139Sjb150015 	    &param->rw_vdb);
427*8934SJose.Borrego@Sun.COM 
428*8934SJose.Borrego@Sun.COM 	if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
4296139Sjb150015 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
4306139Sjb150015 		    ERRDOS, ERROR_INVALID_PARAMETER);
4316139Sjb150015 		return (SDRC_ERROR);
4325331Samw 	}
4335331Samw 
434*8934SJose.Borrego@Sun.COM 	param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
4355331Samw 
4366139Sjb150015 	if (param->rw_count != 0) {
4375331Samw 		if ((rc = smb_write_common(sr, param)) != 0) {
4386139Sjb150015 			if (sr->smb_error.status !=
4396139Sjb150015 			    NT_STATUS_FILE_LOCK_CONFLICT)
4406139Sjb150015 				smbsr_errno(sr, rc);
4416139Sjb150015 			return (SDRC_ERROR);
4425331Samw 		}
4435331Samw 	}
4445331Samw 
445*8934SJose.Borrego@Sun.COM 	count_low = param->rw_count & 0xFFFF;
446*8934SJose.Borrego@Sun.COM 	count_high = (param->rw_count >> 16) & 0xFF;
447*8934SJose.Borrego@Sun.COM 
448*8934SJose.Borrego@Sun.COM 	rc = smbsr_encode_result(sr, 6, 0, "bb1.wwwwww",
449*8934SJose.Borrego@Sun.COM 	    6, sr->andx_com, 15, count_low, 0, count_high, 0, 0);
4505331Samw 
4516139Sjb150015 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
4525331Samw }
4535331Samw 
4545331Samw /*
4555331Samw  * Common function for writing files or IPC/MSRPC named pipes.
4565331Samw  *
4575331Samw  * Returns errno values.
4585331Samw  */
4596030Sjb150015 static int
4606139Sjb150015 smb_write_common(smb_request_t *sr, smb_rw_param_t *param)
4615331Samw {
4625331Samw 	struct smb_ofile *ofile = sr->fid_ofile;
4635331Samw 	smb_node_t *node;
4647052Samw 	int stability = 0;
4655331Samw 	uint32_t lcount;
4665331Samw 	int rc = 0;
4675331Samw 
4685331Samw 	switch (sr->tid_tree->t_res_type & STYPE_MASK) {
4695331Samw 	case STYPE_DISKTREE:
4705331Samw 		node = ofile->f_node;
4715331Samw 
4725331Samw 		if (node->attr.sa_vattr.va_type != VDIR) {
4736139Sjb150015 			rc = smb_lock_range_access(sr, node, param->rw_offset,
4746139Sjb150015 			    param->rw_count, B_TRUE);
4756139Sjb150015 			if (rc != NT_STATUS_SUCCESS) {
4766139Sjb150015 				smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
4776139Sjb150015 				    ERRDOS, ERROR_LOCK_VIOLATION);
4786139Sjb150015 				return (EACCES);
4796139Sjb150015 			}
4805331Samw 		}
4815331Samw 
4826139Sjb150015 		if (SMB_WRMODE_IS_STABLE(param->rw_mode) ||
4835331Samw 		    (node->flags & NODE_FLAGS_WRITE_THROUGH)) {
4847052Samw 			stability = FSYNC;
4855331Samw 		}
4865331Samw 
4875331Samw 		rc = smb_fsop_write(sr, sr->user_cr, node,
488*8934SJose.Borrego@Sun.COM 		    &param->rw_vdb.vdb_uio, &lcount, &node->attr, stability);
4895331Samw 
4905331Samw 		if (rc)
4915331Samw 			return (rc);
4925331Samw 
4935331Samw 		node->flags |= NODE_FLAGS_SYNCATIME;
4945331Samw 
4955331Samw 		if (node->flags & NODE_FLAGS_SET_SIZE) {
4966139Sjb150015 			if ((param->rw_offset + lcount) >= node->n_size) {
4975331Samw 				node->flags &= ~NODE_FLAGS_SET_SIZE;
4986139Sjb150015 				node->n_size = param->rw_offset + lcount;
4995331Samw 			}
5005331Samw 		}
5015331Samw 
502*8934SJose.Borrego@Sun.COM 		param->rw_count = lcount;
5035331Samw 		break;
5045331Samw 
5055331Samw 	case STYPE_IPC:
506*8934SJose.Borrego@Sun.COM 		param->rw_count = param->rw_vdb.vdb_uio.uio_resid;
5075331Samw 
508*8934SJose.Borrego@Sun.COM 		if ((rc = smb_opipe_write(sr, &param->rw_vdb.vdb_uio)) != 0)
5096139Sjb150015 			param->rw_count = 0;
5105331Samw 		break;
5115331Samw 
5125331Samw 	default:
5135331Samw 		rc = EACCES;
5145331Samw 		break;
5155331Samw 	}
5165331Samw 
5175331Samw 	if (rc != 0)
5185331Samw 		return (rc);
5195331Samw 
5205331Samw 	mutex_enter(&ofile->f_mutex);
5216139Sjb150015 	ofile->f_seek_pos = param->rw_offset + param->rw_count;
5225331Samw 	mutex_exit(&ofile->f_mutex);
5235331Samw 	return (rc);
5245331Samw }
5255331Samw 
5265331Samw /*
5275331Samw  * Truncate a disk file to the specified offset.
5285331Samw  * Typically, w_count will be zero here.
5295331Samw  *
5305331Samw  * Returns errno values.
5315331Samw  */
5326030Sjb150015 static int
5336139Sjb150015 smb_write_truncate(smb_request_t *sr, smb_rw_param_t *param)
5345331Samw {
5355331Samw 	struct smb_ofile *ofile = sr->fid_ofile;
5365331Samw 	smb_node_t *node = ofile->f_node;
5375772Sas200622 	boolean_t append_only = B_FALSE;
5386030Sjb150015 	uint32_t status;
5395331Samw 	int rc;
5405331Samw 
5415331Samw 	if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0)
5425331Samw 		return (0);
5435331Samw 
5446030Sjb150015 	status = smb_ofile_access(sr->fid_ofile, sr->user_cr, FILE_WRITE_DATA);
5456030Sjb150015 	if (status != NT_STATUS_SUCCESS) {
5466030Sjb150015 		status = smb_ofile_access(sr->fid_ofile, sr->user_cr,
5475772Sas200622 		    FILE_APPEND_DATA);
5486030Sjb150015 		if (status != NT_STATUS_SUCCESS)
5496030Sjb150015 			return (EACCES);
5506030Sjb150015 		else
5515772Sas200622 			append_only = B_TRUE;
5525772Sas200622 	}
5535772Sas200622 
554*8934SJose.Borrego@Sun.COM 	mutex_enter(&node->n_mutex);
5555772Sas200622 
5566139Sjb150015 	if (append_only && (param->rw_offset < node->n_size)) {
557*8934SJose.Borrego@Sun.COM 		mutex_exit(&node->n_mutex);
5586030Sjb150015 		return (EACCES);
5595772Sas200622 	}
5605772Sas200622 
5615331Samw 	if (node->attr.sa_vattr.va_type != VDIR) {
5626139Sjb150015 		status = smb_lock_range_access(sr, node, param->rw_offset,
5636139Sjb150015 		    param->rw_count, B_TRUE);
5646030Sjb150015 		if (status != NT_STATUS_SUCCESS) {
565*8934SJose.Borrego@Sun.COM 			mutex_exit(&node->n_mutex);
5666139Sjb150015 			smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
5676139Sjb150015 			    ERRDOS, ERROR_LOCK_VIOLATION);
5686030Sjb150015 			return (EACCES);
5695331Samw 		}
5705331Samw 	}
5715331Samw 
5725331Samw 	node->flags |= NODE_FLAGS_SET_SIZE;
5736139Sjb150015 	node->n_size = param->rw_offset;
5745331Samw 
575*8934SJose.Borrego@Sun.COM 	mutex_exit(&node->n_mutex);
5765772Sas200622 
5776600Sas200622 	if ((rc = smb_set_file_size(sr, node)) != 0)
5785331Samw 		return (rc);
5795331Samw 
5805331Samw 	mutex_enter(&ofile->f_mutex);
5816139Sjb150015 	ofile->f_seek_pos = param->rw_offset + param->rw_count;
5825331Samw 	mutex_exit(&ofile->f_mutex);
5835331Samw 	return (0);
5845331Samw }
5855331Samw 
5865331Samw /*
5875331Samw  * Set the file size using the value in the node. The file will only be
5885331Samw  * updated if NODE_FLAGS_SET_SIZE is set.  It is safe to pass a null node
5895331Samw  * pointer, we just return success.
5905331Samw  *
5915331Samw  * The node attributes are refreshed here from the file system. So any
5925331Samw  * attributes that are affected by file size changes, i.e. the mtime,
5935331Samw  * will be current.
5945331Samw  *
5955331Samw  * Note that smb_write_andx cannot be used to reduce the file size so,
5965331Samw  * if this is required, smb_write is called with a count of zero and
5975331Samw  * the appropriate file length in offset. The file should be resized
5985331Samw  * to the length specified by the offset.
5995331Samw  */
6005331Samw int
6016600Sas200622 smb_set_file_size(smb_request_t *sr, smb_node_t *node)
6025331Samw {
6035331Samw 	smb_attr_t new_attr;
6045331Samw 
6056600Sas200622 	if (node == NULL)
6065331Samw 		return (0);
6075331Samw 
6085331Samw 	if ((node->flags & NODE_FLAGS_SET_SIZE) == 0)
6095331Samw 		return (0);
6105331Samw 
6115331Samw 	node->flags &= ~NODE_FLAGS_SET_SIZE;
6125331Samw 
6135331Samw 	bzero(&new_attr, sizeof (new_attr));
6145331Samw 	new_attr.sa_vattr.va_size = node->n_size;
6155331Samw 	new_attr.sa_mask = SMB_AT_SIZE;
6165331Samw 
6177348SJose.Borrego@Sun.COM 	return (smb_fsop_setattr(sr, sr->user_cr, node, &new_attr,
6187348SJose.Borrego@Sun.COM 	    &node->attr));
6195331Samw }
620