xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_read.c (revision d17be682a2c70b4505d43c830bbd2603da11918d)
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 2015-2021 Tintri by DDN, Inc. All rights reserved.
14  */
15 
16 /*
17  * Dispatch function for SMB2_READ
18  * MS-SMB2 sec. 3.3.5.12
19  */
20 
21 #include <smbsrv/smb2_kproto.h>
22 #include <smbsrv/smb_fsops.h>
23 
24 extern boolean_t smb_allow_unbuffered;
25 
26 smb_sdrc_t
27 smb2_read(smb_request_t *sr)
28 {
29 	smb_rw_param_t *param = NULL;
30 	smb_ofile_t *of = NULL;
31 	smb_vdb_t *vdb = NULL;
32 	struct mbuf *m = NULL;
33 	uint16_t StructSize;
34 	uint8_t Padding;
35 	uint8_t Flags;
36 	uint8_t DataOff;
37 	uint32_t Length;
38 	uint64_t Offset;
39 	smb2fid_t smb2fid;
40 	uint32_t MinCount;
41 	uint32_t Channel;
42 	uint32_t Remaining;
43 	uint16_t ChanInfoOffset;
44 	uint16_t ChanInfoLength;
45 	uint32_t XferCount = 0;
46 	uint32_t status;
47 	int rc = 0;
48 	boolean_t unbuffered = B_FALSE;
49 	int ioflag = 0;
50 
51 	/*
52 	 * SMB2 Read request
53 	 */
54 	rc = smb_mbc_decodef(
55 	    &sr->smb_data,
56 	    "wbblqqqlllww",
57 	    &StructSize,		/* w */
58 	    &Padding,			/* b */
59 	    &Flags,			/* b */
60 	    &Length,			/* l */
61 	    &Offset,			/* q */
62 	    &smb2fid.persistent,	/* q */
63 	    &smb2fid.temporal,		/* q */
64 	    &MinCount,			/* l */
65 	    &Channel,			/* l */
66 	    &Remaining,			/* l */
67 	    &ChanInfoOffset,		/* w */
68 	    &ChanInfoLength);		/* w */
69 	if (rc)
70 		return (SDRC_ERROR);
71 	if (StructSize != 49)
72 		return (SDRC_ERROR);
73 
74 	/*
75 	 * Setup an smb_rw_param_t which contains the VDB we need.
76 	 * This is automatically free'd.
77 	 */
78 	param = smb_srm_zalloc(sr, sizeof (*param));
79 	param->rw_offset = Offset;
80 	param->rw_count = Length;
81 	/* Note that the dtrace provider uses sr->arg.rw */
82 	sr->arg.rw = param;
83 
84 	/*
85 	 * Want FID lookup before the start probe.
86 	 */
87 	status = smb2sr_lookup_fid(sr, &smb2fid);
88 	of = sr->fid_ofile;
89 
90 	DTRACE_SMB2_START(op__Read, smb_request_t *, sr); /* arg.rw */
91 
92 	if (status != 0)
93 		goto done; /* Bad FID */
94 
95 	/*
96 	 * Short-circuit zero-byte read, otherwise could panic
97 	 * setting up buffers in smb_mbuf_allocate etc.
98 	 */
99 	if (Length == 0)
100 		goto done;
101 
102 	if (Length > smb2_max_rwsize) {
103 		status = NT_STATUS_INVALID_PARAMETER;
104 		goto done;
105 	}
106 	if (MinCount > Length)
107 		MinCount = Length;
108 
109 	vdb = &param->rw_vdb;
110 	vdb->vdb_tag = 0;
111 	vdb->vdb_uio.uio_iov = &vdb->vdb_iovec[0];
112 	vdb->vdb_uio.uio_iovcnt = MAX_IOVEC;
113 	vdb->vdb_uio.uio_resid = Length;
114 	vdb->vdb_uio.uio_loffset = (offset_t)Offset;
115 	vdb->vdb_uio.uio_segflg = UIO_SYSSPACE;
116 	vdb->vdb_uio.uio_extflg = UIO_COPY_DEFAULT;
117 
118 	sr->raw_data.max_bytes = Length;
119 	m = smb_mbuf_allocate(&vdb->vdb_uio);
120 
121 	/*
122 	 * Unbuffered refers to the MS-FSA Read argument by the same name.
123 	 * It indicates that the cache for this range should be flushed to disk,
124 	 * and data read directly from disk, bypassing the cache.
125 	 * We don't allow that degree of cache management.
126 	 * Translate this directly as FRSYNC,
127 	 * which should at least flush the cache first.
128 	 */
129 
130 	if (smb_allow_unbuffered &&
131 	    (Flags & SMB2_READFLAG_READ_UNBUFFERED) != 0) {
132 		unbuffered = B_TRUE;
133 		ioflag = FRSYNC;
134 	}
135 
136 	switch (of->f_tree->t_res_type & STYPE_MASK) {
137 	case STYPE_DISKTREE:
138 		if (!smb_node_is_dir(of->f_node)) {
139 			/* Check for conflicting locks. */
140 			rc = smb_lock_range_access(sr, of->f_node,
141 			    Offset, Length, B_FALSE);
142 			if (rc) {
143 				rc = ERANGE;
144 				break;
145 			}
146 		}
147 		rc = smb_fsop_read(sr, of->f_cr, of->f_node, of,
148 		    &vdb->vdb_uio, ioflag);
149 		break;
150 	case STYPE_IPC:
151 		if (unbuffered)
152 			rc = EINVAL;
153 		else
154 			rc = smb_opipe_read(sr, &vdb->vdb_uio);
155 		break;
156 	default:
157 	case STYPE_PRINTQ:
158 		rc = EACCES;
159 		break;
160 	}
161 	status = smb_errno2status(rc);
162 
163 	/* How much data we moved. */
164 	XferCount = Length - vdb->vdb_uio.uio_resid;
165 
166 	sr->raw_data.max_bytes = XferCount;
167 	smb_mbuf_trim(m, XferCount);
168 	MBC_ATTACH_MBUF(&sr->raw_data, m);
169 
170 	/*
171 	 * [MS-SMB2] If the read returns fewer bytes than specified by
172 	 * the MinimumCount field of the request, the server MUST fail
173 	 * the request with STATUS_END_OF_FILE
174 	 */
175 	if (status == 0 && XferCount < MinCount)
176 		status = NT_STATUS_END_OF_FILE;
177 
178 	/*
179 	 * Checking the error return _after_ dealing with
180 	 * the returned data so that if m was allocated,
181 	 * it will be free'd via sr->raw_data cleanup.
182 	 */
183 done:
184 	sr->smb2_status = status;
185 	DTRACE_SMB2_DONE(op__Read, smb_request_t *, sr); /* arg.rw */
186 	if (status) {
187 		smb2sr_put_error(sr, status);
188 		return (SDRC_SUCCESS);
189 	}
190 
191 	/*
192 	 * SMB2 Read reply
193 	 */
194 	DataOff = SMB2_HDR_SIZE + 16;
195 	rc = smb_mbc_encodef(
196 	    &sr->reply,
197 	    "wb.lllC",
198 	    17,	/* StructSize */	/* w */
199 	    DataOff,			/* b. */
200 	    XferCount,			/* l */
201 	    0, /* DataRemaining */	/* l */
202 	    0, /* reserved */		/* l */
203 	    &sr->raw_data);		/* C */
204 	if (rc) {
205 		sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
206 		return (SDRC_ERROR);
207 	}
208 
209 	mutex_enter(&of->f_mutex);
210 	of->f_seek_pos = Offset + XferCount;
211 	mutex_exit(&of->f_mutex);
212 
213 	return (SDRC_SUCCESS);
214 }
215