--- old/usr/src/uts/common/fs/smbsrv/smb2_write.c	Mon Aug  5 17:26:26 2019
+++ new/usr/src/uts/common/fs/smbsrv/smb2_write.c	Mon Aug  5 17:26:26 2019
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
@@ -20,6 +20,8 @@
 #include <smbsrv/smb2_kproto.h>
 #include <smbsrv/smb_fsops.h>
 
+boolean_t smb_allow_unbuffered = B_TRUE;
+
 smb_sdrc_t
 smb2_write(smb_request_t *sr)
 {
@@ -40,9 +42,10 @@
 	int data_chain_off, skip;
 	int stability = 0;
 	int rc = 0;
+	boolean_t unbuffered = B_FALSE;
 
 	/*
-	 * SMB2 Write request
+	 * Decode SMB2 Write request
 	 */
 	rc = smb_mbc_decodef(
 	    &sr->smb_data,
@@ -63,44 +66,57 @@
 	if (StructSize != 49)
 		return (SDRC_ERROR);
 
-	status = smb2sr_lookup_fid(sr, &smb2fid);
-	if (status) {
-		smb2sr_put_error(sr, status);
-		return (SDRC_SUCCESS);
-	}
-	of = sr->fid_ofile;
-
-	if (Length > smb2_max_rwsize) {
-		smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
-		return (SDRC_SUCCESS);
-	}
-
 	/*
 	 * Skip any padding before the write data.
 	 */
 	data_chain_off = sr->smb2_cmd_hdr + DataOff;
 	skip = data_chain_off - sr->smb_data.chain_offset;
-	if (skip < 0) {
-		smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
-		return (SDRC_SUCCESS);
-	}
-	if (skip > 0) {
+	if (skip < 0)
+		return (SDRC_ERROR);
+	if (skip > 0)
 		(void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
-	}
 
-	/* This is automatically free'd. */
+	/*
+	 * Decode the write data (payload)
+	 * This is automatically free'd.
+	 */
+	if (Length > smb2_max_rwsize)
+		return (SDRC_ERROR);
 	vdb = smb_srm_zalloc(sr, sizeof (*vdb));
 	rc = smb_mbc_decodef(&sr->smb_data, "#B", Length, vdb);
-	if (rc != 0 || vdb->vdb_len != Length) {
-		smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
-		return (SDRC_SUCCESS);
-	}
+	if (rc != 0 || vdb->vdb_len != Length)
+		return (SDRC_ERROR);
 	vdb->vdb_uio.uio_loffset = (offset_t)Offset;
 
+	/*
+	 * Want FID lookup before the start probe.
+	 */
+	status = smb2sr_lookup_fid(sr, &smb2fid);
+	of = sr->fid_ofile;
+
+	DTRACE_SMB2_START(op__Write, smb_request_t *, sr); /* arg.rw */
+
+	if (status)
+		goto errout; /* Bad FID */
+
+
 	XferCount = 0;
 	if (Length == 0)
-		goto doreply;
+		goto errout;
 
+	/*
+	 * Unbuffered refers to the MS-FSA Write argument by the same name.
+	 * It indicates that the cache for this range should be flushed to disk,
+	 * and data written directly to disk, bypassing the cache.
+	 * We don't allow that degree of cache management.
+	 * Translate this directly as FSYNC,
+	 * which should at least flush the cache.
+	 */
+
+	if (smb_allow_unbuffered &&
+	    (Flags & SMB2_WRITEFLAG_WRITE_UNBUFFERED) != 0)
+		unbuffered = B_TRUE;
+
 	switch (of->f_tree->t_res_type & STYPE_MASK) {
 	case STYPE_DISKTREE:
 	case STYPE_PRINTQ:
@@ -113,21 +129,25 @@
 				break;
 			}
 		}
-		if ((Flags & SMB2_WRITEFLAG_WRITE_THROUGH) ||
-		    (of->f_node->flags & NODE_FLAGS_WRITE_THROUGH)) {
+
+		if (unbuffered || (Flags & SMB2_WRITEFLAG_WRITE_THROUGH) != 0 ||
+		    (of->f_node->flags & NODE_FLAGS_WRITE_THROUGH) != 0) {
 			stability = FSYNC;
 		}
-		rc = smb_fsop_write(sr, of->f_cr, of->f_node,
+		rc = smb_fsop_write(sr, of->f_cr, of->f_node, of,
 		    &vdb->vdb_uio, &XferCount, stability);
 		if (rc)
 			break;
 		of->f_written = B_TRUE;
-		if (!smb_node_is_dir(of->f_node))
-			smb_oplock_break_levelII(of->f_node);
+		/* This revokes read cache delegations. */
+		(void) smb_oplock_break_WRITE(of->f_node, of);
 		break;
 
 	case STYPE_IPC:
-		rc = smb_opipe_write(sr, &vdb->vdb_uio);
+		if (unbuffered || (Flags & SMB2_WRITEFLAG_WRITE_THROUGH) != 0)
+			rc = EINVAL;
+		else
+			rc = smb_opipe_write(sr, &vdb->vdb_uio);
 		if (rc == 0)
 			XferCount = Length;
 		break;
@@ -136,16 +156,20 @@
 		rc = EACCES;
 		break;
 	}
+	status = smb_errno2status(rc);
 
-	if (rc) {
-		smb2sr_put_errno(sr, rc);
+errout:
+	sr->smb2_status = status;
+	DTRACE_SMB2_DONE(op__Write, smb_request_t *, sr); /* arg.rw */
+
+	if (status) {
+		smb2sr_put_error(sr, status);
 		return (SDRC_SUCCESS);
 	}
 
 	/*
-	 * SMB2 Write reply
+	 * Encode SMB2 Write reply
 	 */
-doreply:
 	DataOff = SMB2_HDR_SIZE + 16;
 	rc = smb_mbc_encodef(
 	    &sr->reply, "wwlll",
@@ -154,8 +178,10 @@
 	    XferCount,			/* l */
 	    0, /* DataRemaining */	/* l */
 	    0); /* Channel Info */	/* l */
-	if (rc)
+	if (rc) {
+		sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
 		return (SDRC_ERROR);
+	}
 
 	mutex_enter(&of->f_mutex);
 	of->f_seek_pos = Offset + XferCount;
