xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_read.c (revision 67a4bb8f9ad4c49e9aa9e21e2114a7c093c3a73a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <smbsrv/smb_kproto.h>
26 #include <smbsrv/smb_fsops.h>
27 
28 /*
29  * The maximum number of bytes to return from SMB Core
30  * SmbRead or SmbLockAndRead.
31  */
32 #define	SMB_CORE_READ_MAX	4432
33 
34 /*
35  * The limit in bytes for SmbReadX.
36  */
37 #define	SMB_READX_MAX		0x10000
38 
39 int smb_common_read(smb_request_t *, smb_rw_param_t *);
40 
41 /*
42  * Read bytes from a file or named pipe (SMB Core).
43  *
44  * The requested count specifies the number of bytes desired.  Offset
45  * is limited to 32 bits, so this client request is inappropriate for
46  * files with 64 bit offsets.
47  *
48  * On return, count is the number of bytes actually being returned, which
49  * may be less than the count requested only if a read specifies bytes
50  * beyond the current file size.  In this case only the bytes that exist
51  * are returned.  A read completely beyond the end of file results in a
52  * response of length zero.  This is the only circumstance when a zero
53  * length response is generated.  A count returned which is less than the
54  * count requested is the end of file indicator.
55  */
56 smb_sdrc_t
57 smb_pre_read(smb_request_t *sr)
58 {
59 	smb_rw_param_t *param;
60 	uint32_t off_low;
61 	uint16_t count;
62 	uint16_t remcnt;
63 	int rc;
64 
65 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
66 	sr->arg.rw = param;
67 
68 	rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid,
69 	    &count, &off_low, &remcnt);
70 
71 	param->rw_offset = (uint64_t)off_low;
72 	param->rw_count = (uint32_t)count;
73 	param->rw_mincnt = 0;
74 
75 	DTRACE_SMB_2(op__Read__start, smb_request_t *, sr,
76 	    smb_rw_param_t *, param);
77 
78 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
79 }
80 
81 void
82 smb_post_read(smb_request_t *sr)
83 {
84 	DTRACE_SMB_2(op__Read__done, smb_request_t *, sr,
85 	    smb_rw_param_t *, sr->arg.rw);
86 
87 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
88 }
89 
90 smb_sdrc_t
91 smb_com_read(smb_request_t *sr)
92 {
93 	smb_rw_param_t *param = sr->arg.rw;
94 	uint16_t count;
95 	int rc;
96 
97 	smbsr_lookup_file(sr);
98 	if (sr->fid_ofile == NULL) {
99 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
100 		return (SDRC_ERROR);
101 	}
102 
103 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
104 
105 	if (param->rw_count > SMB_CORE_READ_MAX)
106 		param->rw_count = SMB_CORE_READ_MAX;
107 
108 	if ((rc = smb_common_read(sr, param)) != 0) {
109 		smbsr_errno(sr, rc);
110 		return (SDRC_ERROR);
111 	}
112 
113 	count = (uint16_t)param->rw_count;
114 	rc = smbsr_encode_result(sr, 5, VAR_BCC, "bw8.wbwC",
115 	    5, count, VAR_BCC, 0x01, count, &sr->raw_data);
116 
117 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
118 }
119 
120 /*
121  * Lock and read bytes from a file (SMB Core Plus).  The SmbLockAndRead/
122  * SmbLockAndWrite sub-dialect is only valid on disk files: reject any
123  * attempt to use it on non-disk shares.
124  *
125  * The requested count specifies the number of bytes desired.  Offset
126  * specifies the offset in the file of the first byte to be locked then
127  * read. Note that offset is limited to 32 bits, so this client request
128  * is inappropriate for files with 64 bit offsets.
129  *
130  * As with SMB_LOCK_BYTE_RANGE request, if the lock cannot be granted
131  * immediately an error should be returned to the client.  If an error
132  * occurs on the lock, the bytes should not be read.
133  *
134  * On return, count is the number of bytes actually being returned, which
135  * may be less than the count requested only if a read specifies bytes
136  * beyond the current file size.  In this case only the bytes that exist
137  * are returned.  A read completely beyond the end of file results in a
138  * response of length zero.  This is the only circumstance when a zero
139  * length response is generated.  A count returned which is less than the
140  * count requested is the end of file indicator.
141  */
142 smb_sdrc_t
143 smb_pre_lock_and_read(smb_request_t *sr)
144 {
145 	smb_rw_param_t *param;
146 	uint32_t off_low;
147 	uint16_t count;
148 	uint16_t remcnt;
149 	int rc;
150 
151 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
152 	sr->arg.rw = param;
153 
154 	rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid,
155 	    &count, &off_low, &remcnt);
156 
157 	param->rw_offset = (uint64_t)off_low;
158 	param->rw_count = (uint32_t)count;
159 	param->rw_mincnt = 0;
160 
161 	DTRACE_SMB_2(op__LockAndRead__start, smb_request_t *, sr,
162 	    smb_rw_param_t *, param);
163 
164 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
165 }
166 
167 void
168 smb_post_lock_and_read(smb_request_t *sr)
169 {
170 	DTRACE_SMB_2(op__LockAndRead__done, smb_request_t *, sr,
171 	    smb_rw_param_t *, sr->arg.rw);
172 
173 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
174 }
175 
176 smb_sdrc_t
177 smb_com_lock_and_read(smb_request_t *sr)
178 {
179 	smb_rw_param_t *param = sr->arg.rw;
180 	DWORD status;
181 	uint16_t count;
182 	int rc;
183 
184 	if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
185 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
186 		return (SDRC_ERROR);
187 	}
188 
189 	smbsr_lookup_file(sr);
190 	if (sr->fid_ofile == NULL) {
191 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
192 		return (SDRC_ERROR);
193 	}
194 
195 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
196 
197 	status = smb_lock_range(sr, param->rw_offset, (uint64_t)param->rw_count,
198 	    0, SMB_LOCK_TYPE_READWRITE);
199 
200 	if (status != NT_STATUS_SUCCESS) {
201 		smb_lock_range_error(sr, status);
202 		return (SDRC_ERROR);
203 	}
204 
205 	if (param->rw_count > SMB_CORE_READ_MAX)
206 		param->rw_count = SMB_CORE_READ_MAX;
207 
208 	if ((rc = smb_common_read(sr, param)) != 0) {
209 		smbsr_errno(sr, rc);
210 		return (SDRC_ERROR);
211 	}
212 
213 	count = (uint16_t)param->rw_count;
214 	rc = smbsr_encode_result(sr, 5, VAR_BCC, "bw8.wbwC",
215 	    5, count, VAR_BCC, 0x1, count, &sr->raw_data);
216 
217 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
218 }
219 
220 /*
221  * Read bytes from a file (SMB Core).  This request was extended in
222  * LM 0.12 to support 64-bit offsets, indicated by sending a wct of
223  * 12 and including additional offset information.
224  *
225  * MS-SMB 3.3.5.7 update to LM 0.12 4.2.4:
226  * If wct is 12 and CAP_LARGE_READX is set, the count may be larger
227  * than the negotiated buffer size.  If maxcnt_high is 0xFF, it must
228  * be ignored.  Otherwise, maxcnt_high represents the upper 16 bits
229  * of rw_count.
230  */
231 smb_sdrc_t
232 smb_pre_read_andx(smb_request_t *sr)
233 {
234 	smb_rw_param_t *param;
235 	uint32_t off_low;
236 	uint32_t off_high;
237 	uint32_t maxcnt_high;
238 	uint16_t maxcnt_low;
239 	uint16_t mincnt;
240 	uint16_t remcnt;
241 	int rc;
242 
243 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
244 	sr->arg.rw = param;
245 
246 	if (sr->smb_wct == 12) {
247 		rc = smbsr_decode_vwv(sr, "b3.wlwwlwl", &param->rw_andx,
248 		    &sr->smb_fid, &off_low, &maxcnt_low, &mincnt, &maxcnt_high,
249 		    &remcnt, &off_high);
250 
251 		param->rw_offset = ((uint64_t)off_high << 32) |
252 		    (uint64_t)off_low;
253 
254 		param->rw_count = (uint32_t)maxcnt_low;
255 
256 		if ((sr->session->capabilities & CAP_LARGE_READX) &&
257 		    (maxcnt_high < 0xFF))
258 			param->rw_count |= maxcnt_high << 16;
259 	} else {
260 		rc = smbsr_decode_vwv(sr, "b3.wlwwlw", &param->rw_andx,
261 		    &sr->smb_fid, &off_low, &maxcnt_low, &mincnt, &maxcnt_high,
262 		    &remcnt);
263 
264 		param->rw_offset = (uint64_t)off_low;
265 		param->rw_count = (uint32_t)maxcnt_low;
266 	}
267 
268 	param->rw_mincnt = 0;
269 
270 	DTRACE_SMB_2(op__ReadX__start, smb_request_t *, sr,
271 	    smb_rw_param_t *, param);
272 
273 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
274 }
275 
276 void
277 smb_post_read_andx(smb_request_t *sr)
278 {
279 	DTRACE_SMB_2(op__ReadX__done, smb_request_t *, sr,
280 	    smb_rw_param_t *, sr->arg.rw);
281 
282 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
283 }
284 
285 smb_sdrc_t
286 smb_com_read_andx(smb_request_t *sr)
287 {
288 	smb_rw_param_t *param = sr->arg.rw;
289 	uint16_t datalen_high;
290 	uint16_t datalen_low;
291 	uint16_t data_offset;
292 	uint16_t offset2;
293 	int rc;
294 
295 	smbsr_lookup_file(sr);
296 	if (sr->fid_ofile == NULL) {
297 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
298 		return (SDRC_ERROR);
299 	}
300 
301 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
302 
303 	if (param->rw_count >= SMB_READX_MAX)
304 		param->rw_count = 0;
305 
306 	if ((rc = smb_common_read(sr, param)) != 0) {
307 		smbsr_errno(sr, rc);
308 		return (SDRC_ERROR);
309 	}
310 
311 	datalen_low = param->rw_count & 0xFFFF;
312 	datalen_high = (param->rw_count >> 16) & 0xFF;
313 
314 	/*
315 	 * If this is a secondary command, the data offset
316 	 * includes the previous wct + sizeof(wct).
317 	 */
318 	data_offset = (sr->andx_prev_wct == 0) ? 0 : sr->andx_prev_wct + 1;
319 
320 	if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
321 		data_offset += 60;
322 		offset2 = (param->rw_andx == 0xFF) ? 0 : param->rw_count + 60;
323 
324 		rc = smbsr_encode_result(sr, 12, VAR_BCC, "bb1.ww4.www8.wbC",
325 		    12,			/* wct */
326 		    param->rw_andx,	/* secondary andx command */
327 		    offset2,		/* offset to next command */
328 		    0,			/* set to 0 for named pipes */
329 		    datalen_low,	/* data byte count */
330 		    data_offset,	/* offset from start to data */
331 		    datalen_high,	/* data byte count */
332 		    VAR_BCC,		/* BCC marker */
333 		    0x00,		/* padding */
334 		    &sr->raw_data);
335 	} else {
336 		data_offset += 59;
337 		offset2 = (param->rw_andx == 0xFF) ? 0 : param->rw_count + 59;
338 
339 		rc = smbsr_encode_result(sr, 12, VAR_BCC, "bb1.ww4.www8.wC",
340 		    12,			/* wct */
341 		    param->rw_andx,	/* secondary andx command */
342 		    offset2,		/* offset to next command */
343 		    -1,			/* must be -1 for regular files */
344 		    datalen_low,	/* data byte count */
345 		    data_offset,	/* offset from start to data */
346 		    datalen_high,	/* data byte count */
347 		    VAR_BCC,		/* BCC marker */
348 		    &sr->raw_data);
349 	}
350 
351 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
352 }
353 
354 /*
355  * Common function for reading files or IPC/MSRPC named pipes.  All
356  * protocol read functions should lookup the fid before calling this
357  * function.  We can't move the fid lookup here because lock-and-read
358  * requires the fid to do locking before attempting the read.
359  *
360  * Reading from a file should break oplocks on the file to LEVEL_II.
361  * A call to smb_oplock_break(SMB_OPLOCK_BREAK_TO_LEVEL_II) is not
362  * required as it is a no-op. If there's anything greater than a
363  * LEVEL_II oplock on the file, the oplock MUST be owned by the ofile
364  * on which the read is occuring and therefore would not be broken.
365  *
366  * Returns errno values.
367  */
368 int
369 smb_common_read(smb_request_t *sr, smb_rw_param_t *param)
370 {
371 	smb_ofile_t *ofile = sr->fid_ofile;
372 	smb_node_t *node;
373 	smb_vdb_t *vdb = &param->rw_vdb;
374 	struct mbuf *top;
375 	int rc;
376 
377 	vdb->vdb_tag = 0;
378 	vdb->vdb_uio.uio_iov = &vdb->vdb_iovec[0];
379 	vdb->vdb_uio.uio_iovcnt = MAX_IOVEC;
380 	vdb->vdb_uio.uio_resid = param->rw_count;
381 	vdb->vdb_uio.uio_loffset = (offset_t)param->rw_offset;
382 	vdb->vdb_uio.uio_segflg = UIO_SYSSPACE;
383 	vdb->vdb_uio.uio_extflg = UIO_COPY_DEFAULT;
384 
385 	switch (sr->tid_tree->t_res_type & STYPE_MASK) {
386 	case STYPE_DISKTREE:
387 		node = ofile->f_node;
388 
389 		if (!smb_node_is_dir(node)) {
390 			rc = smb_lock_range_access(sr, node, param->rw_offset,
391 			    param->rw_count, B_FALSE);
392 			if (rc != NT_STATUS_SUCCESS) {
393 				rc = ERANGE;
394 				break;
395 			}
396 		}
397 
398 		if ((ofile->f_flags & SMB_OFLAGS_EXECONLY) &&
399 		    !(sr->smb_flg2 & SMB_FLAGS2_READ_IF_EXECUTE)) {
400 			/*
401 			 * SMB_FLAGS2_READ_IF_EXECUTE: permit execute-only
402 			 * reads.
403 			 *
404 			 * Reject request if the file has been opened
405 			 * execute-only and SMB_FLAGS2_READ_IF_EXECUTE is not
406 			 * set.
407 			 */
408 			rc = EACCES;
409 			break;
410 		}
411 
412 		sr->raw_data.max_bytes = vdb->vdb_uio.uio_resid;
413 		top = smb_mbuf_allocate(&vdb->vdb_uio);
414 
415 		rc = smb_fsop_read(sr, sr->user_cr, node, &vdb->vdb_uio);
416 
417 		sr->raw_data.max_bytes -= vdb->vdb_uio.uio_resid;
418 		smb_mbuf_trim(top, sr->raw_data.max_bytes);
419 		MBC_ATTACH_MBUF(&sr->raw_data, top);
420 		break;
421 
422 	case STYPE_IPC:
423 		rc = smb_opipe_read(sr, &vdb->vdb_uio);
424 		break;
425 
426 	default:
427 		rc = EACCES;
428 		break;
429 	}
430 
431 	param->rw_count -= vdb->vdb_uio.uio_resid;
432 
433 	if (rc != 0)
434 		return (rc);
435 
436 	if (param->rw_mincnt != 0 && param->rw_count < param->rw_mincnt) {
437 		/*
438 		 * mincnt is only used by read-raw and is typically
439 		 * zero.  If mincnt is greater than zero and the
440 		 * number of bytes read is less than mincnt, tell
441 		 * the client that we read nothing.
442 		 */
443 		param->rw_count = 0;
444 	}
445 
446 	param->rw_offset += param->rw_count;
447 	mutex_enter(&sr->fid_ofile->f_mutex);
448 	ofile->f_seek_pos = param->rw_offset;
449 	mutex_exit(&sr->fid_ofile->f_mutex);
450 	return (rc);
451 }
452