xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c (revision 7b07063d906859b2be1e88791f801b3c96e432f6)
1 /*
2  * Copyright (c) 2000-2001 Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Id: smb_smb.c,v 1.35.100.2 2005/06/02 00:55:39 lindak Exp $
33  */
34 
35 /*
36  * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
37  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
38  */
39 
40 /*
41  * various SMB requests. Most of the routines merely packs data into mbufs.
42  */
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kmem.h>
46 #include <sys/proc.h>
47 #include <sys/lock.h>
48 #include <sys/socket.h>
49 #include <sys/uio.h>
50 #include <sys/random.h>
51 #include <sys/note.h>
52 #include <sys/cmn_err.h>
53 
54 #include <netsmb/smb_osdep.h>
55 
56 #include <netsmb/smb.h>
57 #include <netsmb/smb_conn.h>
58 #include <netsmb/smb_rq.h>
59 #include <netsmb/smb_subr.h>
60 #include <netsmb/smb_tran.h>
61 
62 #define	STYPE_LEN	8	/* share type strings */
63 
64 /*
65  * Largest size to use with LARGE_READ/LARGE_WRITE.
66  * Specs say up to 64k data bytes, but Windows traffic
67  * uses 60k... no doubt for some good reason.
68  * (Probably to keep 4k block alignment.)
69  * XXX: Move to smb.h maybe?
70  */
71 #define	SMB_MAX_LARGE_RW_SIZE (60*1024)
72 
73 /*
74  * Default timeout values, all in seconds.
75  * Make these tunable (only via mdb for now).
76  */
77 int smb_timo_notice = 15;
78 int smb_timo_default = 30;	/* was SMB_DEFRQTIMO */
79 int smb_timo_open = 45;
80 int smb_timo_read = 45;
81 int smb_timo_write = 60;	/* was SMBWRTTIMO */
82 int smb_timo_append = 90;
83 
84 static int smb_smb_read(struct smb_share *ssp, uint16_t fid,
85 	uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo);
86 static int smb_smb_write(struct smb_share *ssp, uint16_t fid,
87 	uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo);
88 
89 static int smb_smb_readx(struct smb_share *ssp, uint16_t fid,
90 	uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo);
91 static int smb_smb_writex(struct smb_share *ssp, uint16_t fid,
92 	uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo);
93 
94 /*
95  * Get the string representation of a share "use" type,
96  * as needed for the "service" in tree connect.
97  */
98 static const char *
99 smb_share_typename(uint32_t stype)
100 {
101 	const char *p;
102 
103 	switch (stype) {
104 	case STYPE_DISKTREE:
105 		p = "A:";
106 		break;
107 	case STYPE_PRINTQ:
108 		p = "LPT1:";
109 		break;
110 	case STYPE_DEVICE:
111 		p = "COMM";
112 		break;
113 	case STYPE_IPC:
114 		p = "IPC";
115 		break;
116 	case STYPE_UNKNOWN:
117 	default:
118 		p = "?????";
119 		break;
120 	}
121 	return (p);
122 }
123 
124 /*
125  * Parse a share type name (inverse of above)
126  */
127 static uint32_t
128 smb_share_parsetype(char *name)
129 {
130 	int stype;
131 
132 	switch (*name) {
133 	case 'A':	/* A: */
134 		stype = STYPE_DISKTREE;
135 		break;
136 	case 'C':	/* COMM */
137 		stype = STYPE_DEVICE;
138 		break;
139 	case 'I':	/* IPC */
140 		stype = STYPE_IPC;
141 		break;
142 	case 'L':	/* LPT: */
143 		stype = STYPE_PRINTQ;
144 		break;
145 	default:
146 		stype = STYPE_UNKNOWN;
147 		break;
148 	}
149 	return (stype);
150 }
151 
152 int
153 smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
154 {
155 	struct smb_vc *vcp;
156 	struct smb_rq *rqp = NULL;
157 	struct mbchain *mbp;
158 	struct mdchain *mdp;
159 	const char *tname;
160 	char *pbuf, *unc_name = NULL;
161 	int error, tlen, plen, unc_len;
162 	uint16_t bcnt, options;
163 	uint8_t wc;
164 	char stype_str[STYPE_LEN];
165 
166 	vcp = SSTOVC(ssp);
167 
168 	/*
169 	 * Make this a "VC-level" request, so it will have
170 	 * rqp->sr_share == NULL, and smb_iod_sendrq()
171 	 * will send it with TID = SMB_TID_UNKNOWN
172 	 *
173 	 * This also serves to bypass the wait for
174 	 * share state changes, which this call is
175 	 * trying to carry out.
176 	 */
177 	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_CONNECT_ANDX,
178 	    scred, &rqp);
179 	if (error)
180 		return (error);
181 
182 	/*
183 	 * Build the UNC name, i.e. "//server/share"
184 	 * but with backslashes of course.
185 	 * size math: three slashes, one null.
186 	 */
187 	unc_len = 4 + strlen(vcp->vc_srvname) + strlen(ssp->ss_name);
188 	unc_name = kmem_alloc(unc_len, KM_SLEEP);
189 	(void) snprintf(unc_name, unc_len, "\\\\%s\\%s",
190 	    vcp->vc_srvname, ssp->ss_name);
191 	SMBSDEBUG("unc_name: \"%s\"", unc_name);
192 
193 
194 	/*
195 	 * Share-level password (pre-computed in user-space)
196 	 * MS-SMB 2.2.6 says this should be null terminated,
197 	 * and the pw length includes the null.
198 	 */
199 	pbuf = ssp->ss_pass;
200 	plen = strlen(pbuf) + 1;
201 
202 	/*
203 	 * Build the request.
204 	 */
205 	mbp = &rqp->sr_rq;
206 	smb_rq_wstart(rqp);
207 	mb_put_uint8(mbp, 0xff);
208 	mb_put_uint8(mbp, 0);
209 	mb_put_uint16le(mbp, 0);
210 	mb_put_uint16le(mbp, 0);		/* Flags */
211 	mb_put_uint16le(mbp, plen);
212 	smb_rq_wend(rqp);
213 	smb_rq_bstart(rqp);
214 
215 	/* Tree connect password, if any */
216 	error = mb_put_mem(mbp, pbuf, plen, MB_MSYSTEM);
217 	if (error)
218 		goto out;
219 
220 	/* UNC resource name */
221 	error = smb_put_dstring(mbp, vcp, unc_name, SMB_CS_NONE);
222 	if (error)
223 		goto out;
224 
225 	/*
226 	 * Put the type string (always ASCII),
227 	 * including the null.
228 	 */
229 	tname = smb_share_typename(ssp->ss_use);
230 	tlen = strlen(tname) + 1;
231 	error = mb_put_mem(mbp, tname, tlen, MB_MSYSTEM);
232 	if (error)
233 		goto out;
234 
235 	smb_rq_bend(rqp);
236 
237 	/*
238 	 * Run the request.
239 	 *
240 	 * Using NOINTR_RECV because we don't want to risk
241 	 * missing a successful tree connect response,
242 	 * which would "leak" Tree IDs.
243 	 */
244 	rqp->sr_flags |= SMBR_NOINTR_RECV;
245 	error = smb_rq_simple(rqp);
246 	SMBSDEBUG("%d\n", error);
247 	if (error) {
248 		/*
249 		 * If we get the server name wrong, i.e. due to
250 		 * mis-configured name services, this will be
251 		 * NT_STATUS_DUPLICATE_NAME.  Log this error.
252 		 */
253 		SMBERROR("(%s) failed, status=0x%x",
254 		    unc_name, rqp->sr_error);
255 		goto out;
256 	}
257 
258 	/*
259 	 * Parse the TCON response
260 	 */
261 	smb_rq_getreply(rqp, &mdp);
262 	md_get_uint8(mdp, &wc);
263 	if (wc != 3 && wc != 7) {
264 		error = EBADRPC;
265 		goto out;
266 	}
267 	md_get_uint16le(mdp, NULL);		/* AndX cmd */
268 	md_get_uint16le(mdp, NULL);		/* AndX off */
269 	md_get_uint16le(mdp, &options);		/* option bits (DFS, search) */
270 	if (wc == 7) {
271 		md_get_uint32le(mdp, NULL);	/* MaximalShareAccessRights */
272 		md_get_uint32le(mdp, NULL);	/* GuestMaximalShareAcc... */
273 	}
274 	error = md_get_uint16le(mdp, &bcnt);	/* byte count */
275 	if (error)
276 		goto out;
277 
278 	/*
279 	 * Get the returned share type string, i.e. "IPC" or whatever.
280 	 * (See smb_share_typename, smb_share_parsetype).  If we get
281 	 * an error reading the type, just say STYPE_UNKNOWN.
282 	 */
283 	tlen = STYPE_LEN;
284 	bzero(stype_str, tlen--);
285 	if (tlen > bcnt)
286 		tlen = bcnt;
287 	md_get_mem(mdp, stype_str, tlen, MB_MSYSTEM);
288 	stype_str[tlen] = '\0';
289 	ssp->ss_type = smb_share_parsetype(stype_str);
290 
291 	/* Success! */
292 	SMB_SS_LOCK(ssp);
293 	ssp->ss_tid = rqp->sr_rptid;
294 	ssp->ss_vcgenid = vcp->vc_genid;
295 	ssp->ss_options = options;
296 	ssp->ss_flags |= SMBS_CONNECTED;
297 	SMB_SS_UNLOCK(ssp);
298 
299 out:
300 	if (unc_name)
301 		kmem_free(unc_name, unc_len);
302 	smb_rq_done(rqp);
303 	return (error);
304 }
305 
306 int
307 smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
308 {
309 	struct smb_vc *vcp;
310 	struct smb_rq *rqp;
311 	int error;
312 
313 	if (ssp->ss_tid == SMB_TID_UNKNOWN)
314 		return (0);
315 
316 	/*
317 	 * Build this as a "VC-level" request, so it will
318 	 * avoid testing the _GONE flag on the share,
319 	 * which has already been set at this point.
320 	 * Add the share pointer "by hand" below, so
321 	 * smb_iod_sendrq will plug in the TID.
322 	 */
323 	vcp = SSTOVC(ssp);
324 	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
325 	if (error)
326 		return (error);
327 	rqp->sr_share = ssp; /* by hand */
328 
329 	smb_rq_wstart(rqp);
330 	smb_rq_wend(rqp);
331 	smb_rq_bstart(rqp);
332 	smb_rq_bend(rqp);
333 
334 	/*
335 	 * Run this with a relatively short timeout. (5 sec.)
336 	 * We don't really care about the result here, but we
337 	 * do need to make sure we send this out, or we could
338 	 * "leak" active tree IDs on interrupt or timeout.
339 	 * The NOINTR_SEND flag makes this request immune to
340 	 * interrupt or timeout until the send is done.
341 	 * Also, don't reconnect for this, of course!
342 	 */
343 	rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
344 	error = smb_rq_simple_timed(rqp, 5);
345 	SMBSDEBUG("%d\n", error);
346 	smb_rq_done(rqp);
347 	ssp->ss_tid = SMB_TID_UNKNOWN;
348 	return (error);
349 }
350 
351 /*
352  * Modern create/open of file or directory.
353  */
354 int
355 smb_smb_ntcreate(
356 	struct smb_share *ssp,
357 	struct mbchain	*name_mb,
358 	uint32_t cr_flags,	/* create flags */
359 	uint32_t req_acc,	/* requested access */
360 	uint32_t efa,		/* ext. file attrs (DOS attr +) */
361 	uint32_t share_acc,
362 	uint32_t open_disp,	/* open disposition */
363 	uint32_t createopt,	/* NTCREATEX_OPTIONS_ */
364 	uint32_t impersonate,	/* NTCREATEX_IMPERSONATION_... */
365 	struct smb_cred *scrp,
366 	uint16_t *fidp,		/* returned FID */
367 	uint32_t *cr_act_p,	/* optional create action */
368 	struct smbfattr *fap)	/* optional attributes */
369 {
370 	struct smb_rq rq, *rqp = &rq;
371 	struct smb_vc *vcp = SSTOVC(ssp);
372 	struct mbchain *mbp;
373 	struct mdchain *mdp;
374 	struct smbfattr fa;
375 	uint64_t llongint;
376 	uint32_t longint, createact;
377 	uint16_t fid;
378 	uint8_t wc;
379 	int error;
380 
381 	bzero(&fa, sizeof (fa));
382 	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_NT_CREATE_ANDX, scrp);
383 	if (error)
384 		return (error);
385 	smb_rq_getrequest(rqp, &mbp);
386 
387 	/* Word parameters */
388 	smb_rq_wstart(rqp);
389 	mb_put_uint8(mbp, 0xff);	/* secondary command */
390 	mb_put_uint8(mbp, 0);		/* MBZ */
391 	mb_put_uint16le(mbp, 0);	/* offset to next command (none) */
392 	mb_put_uint8(mbp, 0);		/* MBZ */
393 	mb_put_uint16le(mbp, name_mb->mb_count);
394 	mb_put_uint32le(mbp, cr_flags);	/* NTCREATEX_FLAGS_* */
395 	mb_put_uint32le(mbp, 0);	/* FID - basis for path if not root */
396 	mb_put_uint32le(mbp, req_acc);
397 	mb_put_uint64le(mbp, 0);	/* "initial allocation size" */
398 	mb_put_uint32le(mbp, efa);
399 	mb_put_uint32le(mbp, share_acc);
400 	mb_put_uint32le(mbp, open_disp);
401 	mb_put_uint32le(mbp, createopt);
402 	mb_put_uint32le(mbp, impersonate);
403 	mb_put_uint8(mbp, 0);   /* security flags (?) */
404 	smb_rq_wend(rqp);
405 
406 	/*
407 	 * Byte parameters: Just the path name, aligned.
408 	 * Note: mb_put_mbuf consumes mb_top, so clear it.
409 	 */
410 	smb_rq_bstart(rqp);
411 	if (SMB_UNICODE_STRINGS(vcp))
412 		mb_put_padbyte(mbp);
413 	mb_put_mbuf(mbp, name_mb->mb_top);
414 	bzero(name_mb, sizeof (*name_mb));
415 	smb_rq_bend(rqp);
416 
417 	/*
418 	 * Don't want to risk missing a successful
419 	 * open response, or we could "leak" FIDs.
420 	 */
421 	rqp->sr_flags |= SMBR_NOINTR_RECV;
422 	error = smb_rq_simple_timed(rqp, smb_timo_open);
423 	if (error)
424 		goto done;
425 	smb_rq_getreply(rqp, &mdp);
426 	/*
427 	 * spec says 26 for word count, but 34 words are defined
428 	 * and observed from win2000
429 	 */
430 	error = md_get_uint8(mdp, &wc);
431 	if (error)
432 		goto done;
433 	if (wc != 26 && wc < 34) {
434 		error = EBADRPC;
435 		goto done;
436 	}
437 	md_get_uint8(mdp, NULL);		/* secondary cmd */
438 	md_get_uint8(mdp, NULL);		/* mbz */
439 	md_get_uint16le(mdp, NULL);		/* andxoffset */
440 	md_get_uint8(mdp, NULL);		/* oplock lvl granted */
441 	md_get_uint16le(mdp, &fid);		/* file ID */
442 	md_get_uint32le(mdp, &createact);	/* create_action */
443 
444 	md_get_uint64le(mdp, &llongint);	/* creation time */
445 	smb_time_NT2local(llongint, &fa.fa_createtime);
446 	md_get_uint64le(mdp, &llongint);	/* access time */
447 	smb_time_NT2local(llongint, &fa.fa_atime);
448 	md_get_uint64le(mdp, &llongint);	/* write time */
449 	smb_time_NT2local(llongint, &fa.fa_mtime);
450 	md_get_uint64le(mdp, &llongint);	/* change time */
451 	smb_time_NT2local(llongint, &fa.fa_ctime);
452 
453 	md_get_uint32le(mdp, &longint);		/* attributes */
454 	fa.fa_attr = longint;
455 	md_get_uint64le(mdp, &llongint);	/* allocation size */
456 	fa.fa_allocsz = llongint;
457 	md_get_uint64le(mdp, &llongint);	/* EOF position */
458 	fa.fa_size = llongint;
459 
460 	error = md_get_uint16le(mdp, NULL);	/* file type */
461 	/* other stuff we don't care about */
462 
463 done:
464 	smb_rq_done(rqp);
465 	if (error)
466 		return (error);
467 
468 	*fidp = fid;
469 	if (cr_act_p)
470 		*cr_act_p = createact;
471 	if (fap)
472 		*fap = fa; /* struct copy */
473 
474 	return (0);
475 }
476 
477 int
478 smb_smb_close(struct smb_share *ssp, uint16_t fid, struct timespec *mtime,
479 	struct smb_cred *scrp)
480 {
481 	struct smb_rq rq, *rqp = &rq;
482 	struct mbchain *mbp;
483 	long time;
484 	int error;
485 
486 	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_CLOSE, scrp);
487 	if (error)
488 		return (error);
489 	smb_rq_getrequest(rqp, &mbp);
490 	smb_rq_wstart(rqp);
491 	mb_put_uint16le(mbp, fid);
492 	if (mtime) {
493 		int sv_tz = SSTOVC(ssp)->vc_sopt.sv_tz;
494 		smb_time_local2server(mtime, sv_tz, &time);
495 	} else {
496 		time = 0;
497 	}
498 	mb_put_uint32le(mbp, time);
499 	smb_rq_wend(rqp);
500 	smb_rq_bstart(rqp);
501 	smb_rq_bend(rqp);
502 
503 	/* Make sure we send, but only if already connected */
504 	rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
505 	error = smb_rq_simple(rqp);
506 	smb_rq_done(rqp);
507 	return (error);
508 }
509 
510 int
511 smb_smb_open_prjob(
512 	struct smb_share *ssp,
513 	char	*title,
514 	uint16_t setuplen,
515 	uint16_t mode,
516 	struct smb_cred *scrp,
517 	uint16_t *fidp)
518 {
519 	struct smb_rq rq, *rqp = &rq;
520 	struct smb_vc *vcp = SSTOVC(ssp);
521 	struct mbchain *mbp;
522 	struct mdchain *mdp;
523 	uint16_t fid;
524 	uint8_t wc;
525 	int error;
526 
527 	error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_OPEN_PRINT_FILE, scrp);
528 	if (error)
529 		return (error);
530 	smb_rq_getrequest(rqp, &mbp);
531 
532 	/* Word parameters */
533 	smb_rq_wstart(rqp);
534 	mb_put_uint16le(mbp, setuplen);
535 	mb_put_uint16le(mbp, mode);
536 	smb_rq_wend(rqp);
537 
538 	/*
539 	 * Byte parameters: Just the title
540 	 */
541 	smb_rq_bstart(rqp);
542 	mb_put_uint8(mbp, SMB_DT_ASCII);
543 	error = smb_put_dstring(mbp, vcp, title, SMB_CS_NONE);
544 	smb_rq_bend(rqp);
545 	if (error)
546 		goto done;
547 
548 	/*
549 	 * Don't want to risk missing a successful
550 	 * open response, or we could "leak" FIDs.
551 	 */
552 	rqp->sr_flags |= SMBR_NOINTR_RECV;
553 	error = smb_rq_simple_timed(rqp, smb_timo_open);
554 	if (error)
555 		goto done;
556 
557 	smb_rq_getreply(rqp, &mdp);
558 	error = md_get_uint8(mdp, &wc);
559 	if (error || wc < 1) {
560 		error = EBADRPC;
561 		goto done;
562 	}
563 	error = md_get_uint16le(mdp, &fid);
564 
565 done:
566 	smb_rq_done(rqp);
567 	if (error)
568 		return (error);
569 
570 	*fidp = fid;
571 	return (0);
572 }
573 
574 /*
575  * Like smb_smb_close, but for print shares.
576  */
577 int
578 smb_smb_close_prjob(struct smb_share *ssp, uint16_t fid,
579 	struct smb_cred *scrp)
580 {
581 	struct smb_rq rq, *rqp = &rq;
582 	struct mbchain *mbp;
583 	int error;
584 
585 	error = smb_rq_init(rqp, SSTOCP(ssp),
586 	    SMB_COM_CLOSE_PRINT_FILE, scrp);
587 	if (error)
588 		return (error);
589 	smb_rq_getrequest(rqp, &mbp);
590 	smb_rq_wstart(rqp);
591 	mb_put_uint16le(mbp, fid);
592 	smb_rq_wend(rqp);
593 	smb_rq_bstart(rqp);
594 	smb_rq_bend(rqp);
595 
596 	/* Make sure we send but only if already connected */
597 	rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
598 	error = smb_rq_simple(rqp);
599 	smb_rq_done(rqp);
600 	return (error);
601 }
602 
603 /*
604  * Common function for read/write with UIO.
605  * Called by netsmb smb_usr_rw,
606  *  smbfs_readvnode, smbfs_writevnode
607  */
608 int
609 smb_rwuio(struct smb_share *ssp, uint16_t fid, uio_rw_t rw,
610 	uio_t *uiop, smb_cred_t *scred, int timo)
611 {
612 	struct smb_vc *vcp = SSTOVC(ssp);
613 	ssize_t  save_resid;
614 	uint32_t len, rlen, maxlen;
615 	int error = 0;
616 	int (*iofun)(struct smb_share *, uint16_t, uint32_t *,
617 	    uio_t *, smb_cred_t *, int);
618 
619 	/*
620 	 * Determine which function to use,
621 	 * and the transfer size per call.
622 	 */
623 	if (SMB_DIALECT(vcp) >= SMB_DIALECT_NTLM0_12) {
624 		/*
625 		 * Using NT LM 0.12, so readx, writex.
626 		 * Make sure we can represent the offset.
627 		 */
628 		if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES) == 0 &&
629 		    (uiop->uio_loffset + uiop->uio_resid) > UINT32_MAX)
630 			return (EFBIG);
631 
632 		if (rw == UIO_READ) {
633 			iofun = smb_smb_readx;
634 			if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX)
635 				maxlen = SMB_MAX_LARGE_RW_SIZE;
636 			else
637 				maxlen = vcp->vc_rxmax;
638 		} else { /* UIO_WRITE */
639 			iofun = smb_smb_writex;
640 			if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX)
641 				maxlen = SMB_MAX_LARGE_RW_SIZE;
642 			else
643 				maxlen = vcp->vc_wxmax;
644 		}
645 	} else {
646 		/*
647 		 * Using the old SMB_READ and SMB_WRITE so
648 		 * we're limited to 32-bit offsets, etc.
649 		 * XXX: Someday, punt the old dialects.
650 		 */
651 		if ((uiop->uio_loffset + uiop->uio_resid) > UINT32_MAX)
652 			return (EFBIG);
653 
654 		if (rw == UIO_READ) {
655 			iofun = smb_smb_read;
656 			maxlen = vcp->vc_rxmax;
657 		} else { /* UIO_WRITE */
658 			iofun = smb_smb_write;
659 			maxlen = vcp->vc_wxmax;
660 		}
661 	}
662 
663 	save_resid = uiop->uio_resid;
664 	while (uiop->uio_resid > 0) {
665 		/* Lint: uio_resid may be 64-bits */
666 		rlen = len = (uint32_t)min(maxlen, uiop->uio_resid);
667 		error = (*iofun)(ssp, fid, &rlen, uiop, scred, timo);
668 
669 		/*
670 		 * Note: the iofun called uio_update, so
671 		 * not doing that here as one might expect.
672 		 *
673 		 * Quit the loop either on error, or if we
674 		 * transferred less then requested.
675 		 */
676 		if (error || (rlen < len))
677 			break;
678 
679 		timo = 0; /* only first I/O should wait */
680 	}
681 	if (error && (save_resid != uiop->uio_resid)) {
682 		/*
683 		 * Stopped on an error after having
684 		 * successfully transferred data.
685 		 * Suppress this error.
686 		 */
687 		SMBSDEBUG("error %d suppressed\n", error);
688 		error = 0;
689 	}
690 
691 	return (error);
692 }
693 
694 static int
695 smb_smb_readx(struct smb_share *ssp, uint16_t fid, uint32_t *lenp,
696 	uio_t *uiop, smb_cred_t *scred, int timo)
697 {
698 	struct smb_rq *rqp;
699 	struct mbchain *mbp;
700 	struct mdchain *mdp;
701 	int error;
702 	uint32_t offlo, offhi, rlen;
703 	uint16_t lenhi, lenlo, off, doff;
704 	uint8_t wc;
705 
706 	lenhi = (uint16_t)(*lenp >> 16);
707 	lenlo = (uint16_t)*lenp;
708 	offhi = (uint32_t)(uiop->uio_loffset >> 32);
709 	offlo = (uint32_t)uiop->uio_loffset;
710 
711 	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp);
712 	if (error)
713 		return (error);
714 	smb_rq_getrequest(rqp, &mbp);
715 	smb_rq_wstart(rqp);
716 	mb_put_uint8(mbp, 0xff);	/* no secondary command */
717 	mb_put_uint8(mbp, 0);		/* MBZ */
718 	mb_put_uint16le(mbp, 0);	/* offset to secondary */
719 	mb_put_uint16le(mbp, fid);
720 	mb_put_uint32le(mbp, offlo);	/* offset (low part) */
721 	mb_put_uint16le(mbp, lenlo);	/* MaxCount */
722 	mb_put_uint16le(mbp, 1);	/* MinCount */
723 					/* (only indicates blocking) */
724 	mb_put_uint32le(mbp, lenhi);	/* MaxCountHigh */
725 	mb_put_uint16le(mbp, lenlo);	/* Remaining ("obsolete") */
726 	mb_put_uint32le(mbp, offhi);	/* offset (high part) */
727 	smb_rq_wend(rqp);
728 	smb_rq_bstart(rqp);
729 	smb_rq_bend(rqp);
730 
731 	if (timo == 0)
732 		timo = smb_timo_read;
733 	error = smb_rq_simple_timed(rqp, timo);
734 	if (error)
735 		goto out;
736 
737 	smb_rq_getreply(rqp, &mdp);
738 	error = md_get_uint8(mdp, &wc);
739 	if (error)
740 		goto out;
741 	if (wc != 12) {
742 		error = EBADRPC;
743 		goto out;
744 	}
745 	md_get_uint8(mdp, NULL);
746 	md_get_uint8(mdp, NULL);
747 	md_get_uint16le(mdp, NULL);
748 	md_get_uint16le(mdp, NULL);
749 	md_get_uint16le(mdp, NULL);	/* data compaction mode */
750 	md_get_uint16le(mdp, NULL);
751 	md_get_uint16le(mdp, &lenlo);	/* data len ret. */
752 	md_get_uint16le(mdp, &doff);	/* data offset */
753 	md_get_uint16le(mdp, &lenhi);
754 	rlen = (lenhi << 16) | lenlo;
755 	md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
756 	error = md_get_uint16le(mdp, NULL);	/* ByteCount */
757 	if (error)
758 		goto out;
759 	/*
760 	 * Does the data offset indicate padding?
761 	 * The current offset is a constant, found
762 	 * by counting the md_get_ calls above.
763 	 */
764 	off = SMB_HDRLEN + 3 + (12 * 2); /* =59 */
765 	if (doff > off)	/* pad byte(s)? */
766 		md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM);
767 	if (rlen == 0) {
768 		*lenp = rlen;
769 		goto out;
770 	}
771 	/* paranoid */
772 	if (rlen > *lenp) {
773 		SMBSDEBUG("bad server! rlen %d, len %d\n",
774 		    rlen, *lenp);
775 		rlen = *lenp;
776 	}
777 	error = md_get_uio(mdp, uiop, rlen);
778 	if (error)
779 		goto out;
780 
781 	/* Success */
782 	*lenp = rlen;
783 
784 out:
785 	smb_rq_done(rqp);
786 	return (error);
787 }
788 
789 static int
790 smb_smb_writex(struct smb_share *ssp, uint16_t fid, uint32_t *lenp,
791 	uio_t *uiop, smb_cred_t *scred, int timo)
792 {
793 	struct smb_rq *rqp;
794 	struct mbchain *mbp;
795 	struct mdchain *mdp;
796 	int error;
797 	uint32_t offlo, offhi, rlen;
798 	uint16_t lenhi, lenlo;
799 	uint8_t wc;
800 
801 	lenhi = (uint16_t)(*lenp >> 16);
802 	lenlo = (uint16_t)*lenp;
803 	offhi = (uint32_t)(uiop->uio_loffset >> 32);
804 	offlo = (uint32_t)uiop->uio_loffset;
805 
806 	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp);
807 	if (error)
808 		return (error);
809 	smb_rq_getrequest(rqp, &mbp);
810 	smb_rq_wstart(rqp);
811 	mb_put_uint8(mbp, 0xff);	/* no secondary command */
812 	mb_put_uint8(mbp, 0);		/* MBZ */
813 	mb_put_uint16le(mbp, 0);	/* offset to secondary */
814 	mb_put_uint16le(mbp, fid);
815 	mb_put_uint32le(mbp, offlo);	/* offset (low part) */
816 	mb_put_uint32le(mbp, 0);	/* MBZ (timeout) */
817 	mb_put_uint16le(mbp, 0);	/* !write-thru */
818 	mb_put_uint16le(mbp, 0);
819 	mb_put_uint16le(mbp, lenhi);
820 	mb_put_uint16le(mbp, lenlo);
821 	mb_put_uint16le(mbp, 64);	/* data offset from header start */
822 	mb_put_uint32le(mbp, offhi);	/* offset (high part) */
823 	smb_rq_wend(rqp);
824 	smb_rq_bstart(rqp);
825 
826 	mb_put_uint8(mbp, 0);	/* pad byte */
827 	error = mb_put_uio(mbp, uiop, *lenp);
828 	if (error)
829 		goto out;
830 	smb_rq_bend(rqp);
831 	if (timo == 0)
832 		timo = smb_timo_write;
833 	error = smb_rq_simple_timed(rqp, timo);
834 	if (error)
835 		goto out;
836 	smb_rq_getreply(rqp, &mdp);
837 	error = md_get_uint8(mdp, &wc);
838 	if (error)
839 		goto out;
840 	if (wc != 6) {
841 		error = EBADRPC;
842 		goto out;
843 	}
844 	md_get_uint8(mdp, NULL);	/* andx cmd */
845 	md_get_uint8(mdp, NULL);	/* reserved */
846 	md_get_uint16le(mdp, NULL);	/* andx offset */
847 	md_get_uint16le(mdp, &lenlo);	/* data len ret. */
848 	md_get_uint16le(mdp, NULL);	/* remaining */
849 	error = md_get_uint16le(mdp, &lenhi);
850 	if (error)
851 		goto out;
852 
853 	/* Success */
854 	rlen = (lenhi << 16) | lenlo;
855 	*lenp = rlen;
856 
857 out:
858 	smb_rq_done(rqp);
859 	return (error);
860 }
861 
862 static int
863 smb_smb_read(struct smb_share *ssp, uint16_t fid, uint32_t *lenp,
864 	uio_t *uiop, smb_cred_t *scred, int timo)
865 {
866 	struct smb_rq *rqp;
867 	struct mbchain *mbp;
868 	struct mdchain *mdp;
869 	int error;
870 	uint32_t off32;
871 	uint16_t bc, cnt, dlen, rcnt, todo;
872 	uint8_t wc;
873 
874 	ASSERT(uiop->uio_loffset <= UINT32_MAX);
875 	off32 = (uint32_t)uiop->uio_loffset;
876 	ASSERT(*lenp <= UINT16_MAX);
877 	cnt = (uint16_t)*lenp;
878 	/* This next is an "estimate" of planned reads. */
879 	todo = (uint16_t)min(uiop->uio_resid, UINT16_MAX);
880 
881 	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
882 	if (error)
883 		return (error);
884 	smb_rq_getrequest(rqp, &mbp);
885 	smb_rq_wstart(rqp);
886 	mb_put_uint16le(mbp, fid);
887 	mb_put_uint16le(mbp, cnt);
888 	mb_put_uint32le(mbp, off32);
889 	mb_put_uint16le(mbp, todo);
890 	smb_rq_wend(rqp);
891 	smb_rq_bstart(rqp);
892 	smb_rq_bend(rqp);
893 
894 	if (timo == 0)
895 		timo = smb_timo_read;
896 	error = smb_rq_simple_timed(rqp, timo);
897 	if (error)
898 		goto out;
899 	smb_rq_getreply(rqp, &mdp);
900 	error = md_get_uint8(mdp, &wc);
901 	if (error)
902 		goto out;
903 	if (wc != 5) {
904 		error = EBADRPC;
905 		goto out;
906 	}
907 	md_get_uint16le(mdp, &rcnt);		/* ret. count */
908 	md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);  /* res. */
909 	md_get_uint16le(mdp, &bc);		/* byte count */
910 	md_get_uint8(mdp, NULL);		/* buffer format */
911 	error = md_get_uint16le(mdp, &dlen);	/* data len */
912 	if (error)
913 		goto out;
914 	if (dlen < rcnt) {
915 		SMBSDEBUG("oops: dlen=%d rcnt=%d\n",
916 		    (int)dlen, (int)rcnt);
917 		rcnt = dlen;
918 	}
919 	if (rcnt == 0) {
920 		*lenp = 0;
921 		goto out;
922 	}
923 	/* paranoid */
924 	if (rcnt > cnt) {
925 		SMBSDEBUG("bad server! rcnt %d, cnt %d\n",
926 		    (int)rcnt, (int)cnt);
927 		rcnt = cnt;
928 	}
929 	error = md_get_uio(mdp, uiop, (int)rcnt);
930 	if (error)
931 		goto out;
932 
933 	/* success */
934 	*lenp = (int)rcnt;
935 
936 out:
937 	smb_rq_done(rqp);
938 	return (error);
939 }
940 
941 static int
942 smb_smb_write(struct smb_share *ssp, uint16_t fid, uint32_t *lenp,
943 	uio_t *uiop, smb_cred_t *scred, int timo)
944 {
945 	struct smb_rq *rqp;
946 	struct mbchain *mbp;
947 	struct mdchain *mdp;
948 	int error;
949 	uint32_t off32;
950 	uint16_t cnt, rcnt, todo;
951 	uint8_t wc;
952 
953 	ASSERT(uiop->uio_loffset <= UINT32_MAX);
954 	off32 = (uint32_t)uiop->uio_loffset;
955 	ASSERT(*lenp <= UINT16_MAX);
956 	cnt = (uint16_t)*lenp;
957 	/* This next is an "estimate" of planned writes. */
958 	todo = (uint16_t)min(uiop->uio_resid, UINT16_MAX);
959 
960 	error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
961 	if (error)
962 		return (error);
963 	smb_rq_getrequest(rqp, &mbp);
964 	smb_rq_wstart(rqp);
965 	mb_put_uint16le(mbp, fid);
966 	mb_put_uint16le(mbp, cnt);
967 	mb_put_uint32le(mbp, off32);
968 	mb_put_uint16le(mbp, todo);
969 	smb_rq_wend(rqp);
970 	smb_rq_bstart(rqp);
971 	mb_put_uint8(mbp, SMB_DT_DATA);
972 	mb_put_uint16le(mbp, cnt);
973 
974 	error = mb_put_uio(mbp, uiop, *lenp);
975 	if (error)
976 		goto out;
977 	smb_rq_bend(rqp);
978 	if (timo == 0)
979 		timo = smb_timo_write;
980 	error = smb_rq_simple_timed(rqp, timo);
981 	if (error)
982 		goto out;
983 	smb_rq_getreply(rqp, &mdp);
984 	error = md_get_uint8(mdp, &wc);
985 	if (error)
986 		goto out;
987 	if (wc != 1) {
988 		error = EBADRPC;
989 		goto out;
990 	}
991 	error = md_get_uint16le(mdp, &rcnt);
992 	if (error)
993 		goto out;
994 	*lenp = rcnt;
995 
996 out:
997 	smb_rq_done(rqp);
998 	return (error);
999 }
1000 
1001 
1002 static u_int32_t	smbechoes = 0;
1003 
1004 int
1005 smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo)
1006 {
1007 	struct smb_rq *rqp;
1008 	struct mbchain *mbp;
1009 	int error;
1010 
1011 	error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
1012 	if (error)
1013 		return (error);
1014 	mbp = &rqp->sr_rq;
1015 	smb_rq_wstart(rqp);
1016 	mb_put_uint16le(mbp, 1); /* echo count */
1017 	smb_rq_wend(rqp);
1018 	smb_rq_bstart(rqp);
1019 	mb_put_uint32le(mbp, atomic_inc_32_nv(&smbechoes));
1020 	smb_rq_bend(rqp);
1021 	/*
1022 	 * Note: the IOD calls this, so
1023 	 * this request must not wait for
1024 	 * connection state changes, etc.
1025 	 */
1026 	rqp->sr_flags |= SMBR_NORECONNECT;
1027 	error = smb_rq_simple_timed(rqp, timo);
1028 	SMBSDEBUG("%d\n", error);
1029 	smb_rq_done(rqp);
1030 	return (error);
1031 }
1032