xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_write.c (revision c4ccc1f9004b70b07e4cdb57641c38ab607306c9)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
14  */
15 
16 /*
17  * Dispatch function for SMB2_WRITE
18  */
19 
20 #include <smbsrv/smb2_kproto.h>
21 #include <smbsrv/smb_fsops.h>
22 
23 smb_sdrc_t
24 smb2_write(smb_request_t *sr)
25 {
26 	smb_rw_param_t *param = NULL;
27 	smb_ofile_t *of = NULL;
28 	smb_vdb_t *vdb = NULL;
29 	uint16_t StructSize;
30 	uint16_t DataOff;
31 	uint32_t Length;
32 	uint64_t Offset;
33 	smb2fid_t smb2fid;
34 	uint32_t Channel;
35 	uint32_t Remaining;
36 	uint16_t ChanInfoOffset;
37 	uint16_t ChanInfoLength;
38 	uint32_t Flags;
39 	uint32_t XferCount;
40 	uint32_t status;
41 	int data_chain_off, skip;
42 	int stability = 0;
43 	int rc = 0;
44 
45 	/*
46 	 * Decode SMB2 Write request
47 	 */
48 	rc = smb_mbc_decodef(
49 	    &sr->smb_data,
50 	    "wwlqqqllwwl",
51 	    &StructSize,		/* w */
52 	    &DataOff,			/* w */
53 	    &Length,			/* l */
54 	    &Offset,			/* q */
55 	    &smb2fid.persistent,	/* q */
56 	    &smb2fid.temporal,		/* q */
57 	    &Channel,			/* l */
58 	    &Remaining,			/* l */
59 	    &ChanInfoOffset,		/* w */
60 	    &ChanInfoLength,		/* w */
61 	    &Flags);			/* l */
62 	if (rc)
63 		return (SDRC_ERROR);
64 	if (StructSize != 49)
65 		return (SDRC_ERROR);
66 
67 	/*
68 	 * Setup an smb_rw_param_t which contains the VDB we need.
69 	 * This is automatically free'd.
70 	 */
71 	param = smb_srm_zalloc(sr, sizeof (*param));
72 	param->rw_offset = Offset;
73 	param->rw_count = Length;
74 	/* Note that the dtrace provider uses sr->arg.rw */
75 	sr->arg.rw = param;
76 
77 	/*
78 	 * Skip any padding before the write data.
79 	 */
80 	data_chain_off = sr->smb2_cmd_hdr + DataOff;
81 	skip = data_chain_off - sr->smb_data.chain_offset;
82 	if (skip < 0)
83 		return (SDRC_ERROR);
84 	if (skip > 0)
85 		(void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
86 
87 	/*
88 	 * Decode the write data (payload)
89 	 */
90 	if (Length > smb2_max_rwsize)
91 		return (SDRC_ERROR);
92 	vdb = &param->rw_vdb;
93 	rc = smb_mbc_decodef(&sr->smb_data, "#B", Length, vdb);
94 	if (rc != 0 || vdb->vdb_len != Length)
95 		return (SDRC_ERROR);
96 	vdb->vdb_uio.uio_loffset = (offset_t)Offset;
97 
98 	/*
99 	 * Want FID lookup before the start probe.
100 	 */
101 	status = smb2sr_lookup_fid(sr, &smb2fid);
102 	of = sr->fid_ofile;
103 
104 	DTRACE_SMB2_START(op__Write, smb_request_t *, sr); /* arg.rw */
105 
106 	if (status)
107 		goto errout; /* Bad FID */
108 
109 
110 	XferCount = 0;
111 	if (Length == 0)
112 		goto errout;
113 
114 	switch (of->f_tree->t_res_type & STYPE_MASK) {
115 	case STYPE_DISKTREE:
116 	case STYPE_PRINTQ:
117 		if (!smb_node_is_dir(of->f_node)) {
118 			/* Check for conflicting locks. */
119 			rc = smb_lock_range_access(sr, of->f_node,
120 			    Offset, Length, B_TRUE);
121 			if (rc) {
122 				rc = ERANGE;
123 				break;
124 			}
125 		}
126 		if ((Flags & SMB2_WRITEFLAG_WRITE_THROUGH) ||
127 		    (of->f_node->flags & NODE_FLAGS_WRITE_THROUGH)) {
128 			stability = FSYNC;
129 		}
130 		rc = smb_fsop_write(sr, of->f_cr, of->f_node, of,
131 		    &vdb->vdb_uio, &XferCount, stability);
132 		if (rc)
133 			break;
134 		of->f_written = B_TRUE;
135 		/* This revokes read cache delegations. */
136 		(void) smb_oplock_break_WRITE(of->f_node, of);
137 		break;
138 
139 	case STYPE_IPC:
140 		rc = smb_opipe_write(sr, &vdb->vdb_uio);
141 		if (rc == 0)
142 			XferCount = Length;
143 		break;
144 
145 	default:
146 		rc = EACCES;
147 		break;
148 	}
149 	status = smb_errno2status(rc);
150 
151 errout:
152 	sr->smb2_status = status;
153 	DTRACE_SMB2_DONE(op__Write, smb_request_t *, sr); /* arg.rw */
154 
155 	if (status) {
156 		smb2sr_put_error(sr, status);
157 		return (SDRC_SUCCESS);
158 	}
159 
160 	/*
161 	 * Encode SMB2 Write reply
162 	 */
163 	DataOff = SMB2_HDR_SIZE + 16;
164 	rc = smb_mbc_encodef(
165 	    &sr->reply, "wwlll",
166 	    17,	/* StructSize */	/* w */
167 	    0, /* reserved */		/* w */
168 	    XferCount,			/* l */
169 	    0, /* DataRemaining */	/* l */
170 	    0); /* Channel Info */	/* l */
171 	if (rc) {
172 		sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
173 		return (SDRC_ERROR);
174 	}
175 
176 	mutex_enter(&of->f_mutex);
177 	of->f_seek_pos = Offset + XferCount;
178 	mutex_exit(&of->f_mutex);
179 
180 	return (SDRC_SUCCESS);
181 }
182