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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
37 */
38
39 /*
40 * various SMB requests. Most of the routines merely packs data into mbufs.
41 */
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kmem.h>
45 #include <sys/proc.h>
46 #include <sys/lock.h>
47 #include <sys/socket.h>
48 #include <sys/uio.h>
49 #include <sys/random.h>
50 #include <sys/note.h>
51 #include <sys/cmn_err.h>
52
53 #include <netsmb/smb_osdep.h>
54
55 #include <netsmb/smb.h>
56 #include <netsmb/smb_conn.h>
57 #include <netsmb/smb_rq.h>
58 #include <netsmb/smb_subr.h>
59 #include <netsmb/smb_tran.h>
60
61 /*
62 * Largest size to use with LARGE_READ/LARGE_WRITE.
63 * Specs say up to 64k data bytes, but Windows traffic
64 * uses 60k... no doubt for some good reason.
65 * (Probably to keep 4k block alignment.)
66 * XXX: Move to smb.h maybe?
67 */
68 #define SMB_MAX_LARGE_RW_SIZE (60*1024)
69
70 /*
71 * Default timeout values, all in seconds.
72 * Make these tunable (only via mdb for now).
73 */
74 int smb_timo_notice = 15;
75 int smb_timo_default = 30; /* was SMB_DEFRQTIMO */
76 int smb_timo_open = 45;
77 int smb_timo_read = 45;
78 int smb_timo_write = 60; /* was SMBWRTTIMO */
79 int smb_timo_append = 90;
80
81 static int smb_smb_read(struct smb_share *ssp, uint16_t fid,
82 uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo);
83 static int smb_smb_write(struct smb_share *ssp, uint16_t fid,
84 uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo);
85
86 static int smb_smb_readx(struct smb_share *ssp, uint16_t fid,
87 uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo);
88 static int smb_smb_writex(struct smb_share *ssp, uint16_t fid,
89 uint32_t *lenp, uio_t *uiop, smb_cred_t *scred, int timo);
90
91 int
smb_smb_treeconnect(struct smb_share * ssp,struct smb_cred * scred)92 smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
93 {
94 struct smb_vc *vcp;
95 struct smb_rq *rqp = NULL;
96 struct mbchain *mbp;
97 struct mdchain *mdp;
98 char *pbuf, *unc_name = NULL;
99 int error, tlen, plen, unc_len;
100 uint16_t bcnt, options;
101 uint8_t wc;
102
103 vcp = SSTOVC(ssp);
104
105 /*
106 * Make this a "VC-level" request, so it will have
107 * rqp->sr_share == NULL, and smb_iod_sendrq()
108 * will send it with TID = SMB_TID_UNKNOWN
109 *
110 * This also serves to bypass the wait for
111 * share state changes, which this call is
112 * trying to carry out.
113 */
114 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_CONNECT_ANDX,
115 scred, &rqp);
116 if (error)
117 return (error);
118
119 /*
120 * Build the UNC name, i.e. "//server/share"
121 * but with backslashes of course.
122 * size math: three slashes, one null.
123 */
124 unc_len = 4 + strlen(vcp->vc_srvname) + strlen(ssp->ss_name);
125 unc_name = kmem_alloc(unc_len, KM_SLEEP);
126 (void) snprintf(unc_name, unc_len, "\\\\%s\\%s",
127 vcp->vc_srvname, ssp->ss_name);
128 SMBSDEBUG("unc_name: \"%s\"", unc_name);
129
130 /*
131 * The password is now pre-computed in the
132 * user-space helper process.
133 */
134 plen = ssp->ss_pwlen;
135 pbuf = ssp->ss_pass;
136
137 /*
138 * Build the request.
139 */
140 mbp = &rqp->sr_rq;
141 smb_rq_wstart(rqp);
142 mb_put_uint8(mbp, 0xff);
143 mb_put_uint8(mbp, 0);
144 mb_put_uint16le(mbp, 0);
145 mb_put_uint16le(mbp, 0); /* Flags */
146 mb_put_uint16le(mbp, plen);
147 smb_rq_wend(rqp);
148 smb_rq_bstart(rqp);
149
150 /* Tree connect password, if any */
151 error = mb_put_mem(mbp, pbuf, plen, MB_MSYSTEM);
152 if (error)
153 goto out;
154
155 /* UNC resource name */
156 error = smb_put_dstring(mbp, vcp, unc_name, SMB_CS_NONE);
157 if (error)
158 goto out;
159
160 /*
161 * Put the type string (always ASCII),
162 * including the null.
163 */
164 tlen = strlen(ssp->ss_type_req) + 1;
165 error = mb_put_mem(mbp, ssp->ss_type_req, tlen, MB_MSYSTEM);
166 if (error)
167 goto out;
168
169 smb_rq_bend(rqp);
170
171 /*
172 * Run the request.
173 *
174 * Using NOINTR_RECV because we don't want to risk
175 * missing a successful tree connect response,
176 * which would "leak" Tree IDs.
177 */
178 rqp->sr_flags |= SMBR_NOINTR_RECV;
179 error = smb_rq_simple(rqp);
180 SMBSDEBUG("%d\n", error);
181 if (error) {
182 /*
183 * If we get the server name wrong, i.e. due to
184 * mis-configured name services, this will be
185 * NT_STATUS_DUPLICATE_NAME. Log this error.
186 */
187 SMBERROR("(%s) failed, status=0x%x",
188 unc_name, rqp->sr_error);
189 goto out;
190 }
191
192 /*
193 * Parse the TCON response
194 */
195 smb_rq_getreply(rqp, &mdp);
196 md_get_uint8(mdp, &wc);
197 if (wc != 3 && wc != 7) {
198 error = EBADRPC;
199 goto out;
200 }
201 md_get_uint16le(mdp, NULL); /* AndX cmd */
202 md_get_uint16le(mdp, NULL); /* AndX off */
203 md_get_uint16le(mdp, &options); /* option bits (DFS, search) */
204 if (wc == 7) {
205 md_get_uint32le(mdp, NULL); /* MaximalShareAccessRights */
206 md_get_uint32le(mdp, NULL); /* GuestMaximalShareAcc... */
207 }
208 error = md_get_uint16le(mdp, &bcnt); /* byte count */
209 if (error)
210 goto out;
211
212 /*
213 * Get the returned share type string,
214 * i.e. "IPC" or whatever. Don't care
215 * if we get an error reading the type.
216 */
217 tlen = sizeof (ssp->ss_type_ret);
218 bzero(ssp->ss_type_ret, tlen--);
219 if (tlen > bcnt)
220 tlen = bcnt;
221 md_get_mem(mdp, ssp->ss_type_ret, tlen, MB_MSYSTEM);
222
223 /* Success! */
224 SMB_SS_LOCK(ssp);
225 ssp->ss_tid = rqp->sr_rptid;
226 ssp->ss_vcgenid = vcp->vc_genid;
227 ssp->ss_options = options;
228 ssp->ss_flags |= SMBS_CONNECTED;
229 SMB_SS_UNLOCK(ssp);
230
231 out:
232 if (unc_name)
233 kmem_free(unc_name, unc_len);
234 smb_rq_done(rqp);
235 return (error);
236 }
237
238 int
smb_smb_treedisconnect(struct smb_share * ssp,struct smb_cred * scred)239 smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
240 {
241 struct smb_vc *vcp;
242 struct smb_rq *rqp;
243 int error;
244
245 if (ssp->ss_tid == SMB_TID_UNKNOWN)
246 return (0);
247
248 /*
249 * Build this as a "VC-level" request, so it will
250 * avoid testing the _GONE flag on the share,
251 * which has already been set at this point.
252 * Add the share pointer "by hand" below, so
253 * smb_iod_sendrq will plug in the TID.
254 */
255 vcp = SSTOVC(ssp);
256 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
257 if (error)
258 return (error);
259 rqp->sr_share = ssp; /* by hand */
260
261 smb_rq_wstart(rqp);
262 smb_rq_wend(rqp);
263 smb_rq_bstart(rqp);
264 smb_rq_bend(rqp);
265
266 /*
267 * Run this with a relatively short timeout. (5 sec.)
268 * We don't really care about the result here, but we
269 * do need to make sure we send this out, or we could
270 * "leak" active tree IDs on interrupt or timeout.
271 * The NOINTR_SEND flag makes this request immune to
272 * interrupt or timeout until the send is done.
273 * Also, don't reconnect for this, of course!
274 */
275 rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT);
276 error = smb_rq_simple_timed(rqp, 5);
277 SMBSDEBUG("%d\n", error);
278 smb_rq_done(rqp);
279 ssp->ss_tid = SMB_TID_UNKNOWN;
280 return (error);
281 }
282
283 /*
284 * Common function for read/write with UIO.
285 * Called by netsmb smb_usr_rw,
286 * smbfs_readvnode, smbfs_writevnode
287 */
288 int
smb_rwuio(struct smb_share * ssp,uint16_t fid,uio_rw_t rw,uio_t * uiop,smb_cred_t * scred,int timo)289 smb_rwuio(struct smb_share *ssp, uint16_t fid, uio_rw_t rw,
290 uio_t *uiop, smb_cred_t *scred, int timo)
291 {
292 struct smb_vc *vcp = SSTOVC(ssp);
293 ssize_t save_resid;
294 uint32_t len, rlen, maxlen;
295 int error = 0;
296 int (*iofun)(struct smb_share *, uint16_t, uint32_t *,
297 uio_t *, smb_cred_t *, int);
298
299 /*
300 * Determine which function to use,
301 * and the transfer size per call.
302 */
303 if (SMB_DIALECT(vcp) >= SMB_DIALECT_NTLM0_12) {
304 /*
305 * Using NT LM 0.12, so readx, writex.
306 * Make sure we can represent the offset.
307 */
308 if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_FILES) == 0 &&
309 (uiop->uio_loffset + uiop->uio_resid) > UINT32_MAX)
310 return (EFBIG);
311
312 if (rw == UIO_READ) {
313 iofun = smb_smb_readx;
314 if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX)
315 maxlen = SMB_MAX_LARGE_RW_SIZE;
316 else
317 maxlen = vcp->vc_rxmax;
318 } else { /* UIO_WRITE */
319 iofun = smb_smb_writex;
320 if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX)
321 maxlen = SMB_MAX_LARGE_RW_SIZE;
322 else
323 maxlen = vcp->vc_wxmax;
324 }
325 } else {
326 /*
327 * Using the old SMB_READ and SMB_WRITE so
328 * we're limited to 32-bit offsets, etc.
329 * XXX: Someday, punt the old dialects.
330 */
331 if ((uiop->uio_loffset + uiop->uio_resid) > UINT32_MAX)
332 return (EFBIG);
333
334 if (rw == UIO_READ) {
335 iofun = smb_smb_read;
336 maxlen = vcp->vc_rxmax;
337 } else { /* UIO_WRITE */
338 iofun = smb_smb_write;
339 maxlen = vcp->vc_wxmax;
340 }
341 }
342
343 save_resid = uiop->uio_resid;
344 while (uiop->uio_resid > 0) {
345 /* Lint: uio_resid may be 64-bits */
346 rlen = len = (uint32_t)min(maxlen, uiop->uio_resid);
347 error = (*iofun)(ssp, fid, &rlen, uiop, scred, timo);
348
349 /*
350 * Note: the iofun called uio_update, so
351 * not doing that here as one might expect.
352 *
353 * Quit the loop either on error, or if we
354 * transferred less then requested.
355 */
356 if (error || (rlen < len))
357 break;
358
359 timo = 0; /* only first I/O should wait */
360 }
361 if (error && (save_resid != uiop->uio_resid)) {
362 /*
363 * Stopped on an error after having
364 * successfully transferred data.
365 * Suppress this error.
366 */
367 SMBSDEBUG("error %d suppressed\n", error);
368 error = 0;
369 }
370
371 return (error);
372 }
373
374 static int
smb_smb_readx(struct smb_share * ssp,uint16_t fid,uint32_t * lenp,uio_t * uiop,smb_cred_t * scred,int timo)375 smb_smb_readx(struct smb_share *ssp, uint16_t fid, uint32_t *lenp,
376 uio_t *uiop, smb_cred_t *scred, int timo)
377 {
378 struct smb_rq *rqp;
379 struct mbchain *mbp;
380 struct mdchain *mdp;
381 int error;
382 uint32_t offlo, offhi, rlen;
383 uint16_t lenhi, lenlo, off, doff;
384 uint8_t wc;
385
386 lenhi = (uint16_t)(*lenp >> 16);
387 lenlo = (uint16_t)*lenp;
388 offhi = (uint32_t)(uiop->uio_loffset >> 32);
389 offlo = (uint32_t)uiop->uio_loffset;
390
391 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ_ANDX, scred, &rqp);
392 if (error)
393 return (error);
394 smb_rq_getrequest(rqp, &mbp);
395 smb_rq_wstart(rqp);
396 mb_put_uint8(mbp, 0xff); /* no secondary command */
397 mb_put_uint8(mbp, 0); /* MBZ */
398 mb_put_uint16le(mbp, 0); /* offset to secondary */
399 mb_put_uint16le(mbp, fid);
400 mb_put_uint32le(mbp, offlo); /* offset (low part) */
401 mb_put_uint16le(mbp, lenlo); /* MaxCount */
402 mb_put_uint16le(mbp, 1); /* MinCount */
403 /* (only indicates blocking) */
404 mb_put_uint32le(mbp, lenhi); /* MaxCountHigh */
405 mb_put_uint16le(mbp, lenlo); /* Remaining ("obsolete") */
406 mb_put_uint32le(mbp, offhi); /* offset (high part) */
407 smb_rq_wend(rqp);
408 smb_rq_bstart(rqp);
409 smb_rq_bend(rqp);
410
411 if (timo == 0)
412 timo = smb_timo_read;
413 error = smb_rq_simple_timed(rqp, timo);
414 if (error)
415 goto out;
416
417 smb_rq_getreply(rqp, &mdp);
418 error = md_get_uint8(mdp, &wc);
419 if (error)
420 goto out;
421 if (wc != 12) {
422 error = EBADRPC;
423 goto out;
424 }
425 md_get_uint8(mdp, NULL);
426 md_get_uint8(mdp, NULL);
427 md_get_uint16le(mdp, NULL);
428 md_get_uint16le(mdp, NULL);
429 md_get_uint16le(mdp, NULL); /* data compaction mode */
430 md_get_uint16le(mdp, NULL);
431 md_get_uint16le(mdp, &lenlo); /* data len ret. */
432 md_get_uint16le(mdp, &doff); /* data offset */
433 md_get_uint16le(mdp, &lenhi);
434 rlen = (lenhi << 16) | lenlo;
435 md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
436 error = md_get_uint16le(mdp, NULL); /* ByteCount */
437 if (error)
438 goto out;
439 /*
440 * Does the data offset indicate padding?
441 * The current offset is a constant, found
442 * by counting the md_get_ calls above.
443 */
444 off = SMB_HDRLEN + 3 + (12 * 2); /* =59 */
445 if (doff > off) /* pad byte(s)? */
446 md_get_mem(mdp, NULL, doff - off, MB_MSYSTEM);
447 if (rlen == 0) {
448 *lenp = rlen;
449 goto out;
450 }
451 /* paranoid */
452 if (rlen > *lenp) {
453 SMBSDEBUG("bad server! rlen %d, len %d\n",
454 rlen, *lenp);
455 rlen = *lenp;
456 }
457 error = md_get_uio(mdp, uiop, rlen);
458 if (error)
459 goto out;
460
461 /* Success */
462 *lenp = rlen;
463
464 out:
465 smb_rq_done(rqp);
466 return (error);
467 }
468
469 static int
smb_smb_writex(struct smb_share * ssp,uint16_t fid,uint32_t * lenp,uio_t * uiop,smb_cred_t * scred,int timo)470 smb_smb_writex(struct smb_share *ssp, uint16_t fid, uint32_t *lenp,
471 uio_t *uiop, smb_cred_t *scred, int timo)
472 {
473 struct smb_rq *rqp;
474 struct mbchain *mbp;
475 struct mdchain *mdp;
476 int error;
477 uint32_t offlo, offhi, rlen;
478 uint16_t lenhi, lenlo;
479 uint8_t wc;
480
481 lenhi = (uint16_t)(*lenp >> 16);
482 lenlo = (uint16_t)*lenp;
483 offhi = (uint32_t)(uiop->uio_loffset >> 32);
484 offlo = (uint32_t)uiop->uio_loffset;
485
486 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE_ANDX, scred, &rqp);
487 if (error)
488 return (error);
489 smb_rq_getrequest(rqp, &mbp);
490 smb_rq_wstart(rqp);
491 mb_put_uint8(mbp, 0xff); /* no secondary command */
492 mb_put_uint8(mbp, 0); /* MBZ */
493 mb_put_uint16le(mbp, 0); /* offset to secondary */
494 mb_put_uint16le(mbp, fid);
495 mb_put_uint32le(mbp, offlo); /* offset (low part) */
496 mb_put_uint32le(mbp, 0); /* MBZ (timeout) */
497 mb_put_uint16le(mbp, 0); /* !write-thru */
498 mb_put_uint16le(mbp, 0);
499 mb_put_uint16le(mbp, lenhi);
500 mb_put_uint16le(mbp, lenlo);
501 mb_put_uint16le(mbp, 64); /* data offset from header start */
502 mb_put_uint32le(mbp, offhi); /* offset (high part) */
503 smb_rq_wend(rqp);
504 smb_rq_bstart(rqp);
505
506 mb_put_uint8(mbp, 0); /* pad byte */
507 error = mb_put_uio(mbp, uiop, *lenp);
508 if (error)
509 goto out;
510 smb_rq_bend(rqp);
511 if (timo == 0)
512 timo = smb_timo_write;
513 error = smb_rq_simple_timed(rqp, timo);
514 if (error)
515 goto out;
516 smb_rq_getreply(rqp, &mdp);
517 error = md_get_uint8(mdp, &wc);
518 if (error)
519 goto out;
520 if (wc != 6) {
521 error = EBADRPC;
522 goto out;
523 }
524 md_get_uint8(mdp, NULL); /* andx cmd */
525 md_get_uint8(mdp, NULL); /* reserved */
526 md_get_uint16le(mdp, NULL); /* andx offset */
527 md_get_uint16le(mdp, &lenlo); /* data len ret. */
528 md_get_uint16le(mdp, NULL); /* remaining */
529 error = md_get_uint16le(mdp, &lenhi);
530 if (error)
531 goto out;
532
533 /* Success */
534 rlen = (lenhi << 16) | lenlo;
535 *lenp = rlen;
536
537 out:
538 smb_rq_done(rqp);
539 return (error);
540 }
541
542 static int
smb_smb_read(struct smb_share * ssp,uint16_t fid,uint32_t * lenp,uio_t * uiop,smb_cred_t * scred,int timo)543 smb_smb_read(struct smb_share *ssp, uint16_t fid, uint32_t *lenp,
544 uio_t *uiop, smb_cred_t *scred, int timo)
545 {
546 struct smb_rq *rqp;
547 struct mbchain *mbp;
548 struct mdchain *mdp;
549 int error;
550 uint32_t off32;
551 uint16_t bc, cnt, dlen, rcnt, todo;
552 uint8_t wc;
553
554 ASSERT(uiop->uio_loffset <= UINT32_MAX);
555 off32 = (uint32_t)uiop->uio_loffset;
556 ASSERT(*lenp <= UINT16_MAX);
557 cnt = (uint16_t)*lenp;
558 /* This next is an "estimate" of planned reads. */
559 todo = (uint16_t)min(uiop->uio_resid, UINT16_MAX);
560
561 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
562 if (error)
563 return (error);
564 smb_rq_getrequest(rqp, &mbp);
565 smb_rq_wstart(rqp);
566 mb_put_uint16le(mbp, fid);
567 mb_put_uint16le(mbp, cnt);
568 mb_put_uint32le(mbp, off32);
569 mb_put_uint16le(mbp, todo);
570 smb_rq_wend(rqp);
571 smb_rq_bstart(rqp);
572 smb_rq_bend(rqp);
573
574 if (timo == 0)
575 timo = smb_timo_read;
576 error = smb_rq_simple_timed(rqp, timo);
577 if (error)
578 goto out;
579 smb_rq_getreply(rqp, &mdp);
580 error = md_get_uint8(mdp, &wc);
581 if (error)
582 goto out;
583 if (wc != 5) {
584 error = EBADRPC;
585 goto out;
586 }
587 md_get_uint16le(mdp, &rcnt); /* ret. count */
588 md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM); /* res. */
589 md_get_uint16le(mdp, &bc); /* byte count */
590 md_get_uint8(mdp, NULL); /* buffer format */
591 error = md_get_uint16le(mdp, &dlen); /* data len */
592 if (error)
593 goto out;
594 if (dlen < rcnt) {
595 SMBSDEBUG("oops: dlen=%d rcnt=%d\n",
596 (int)dlen, (int)rcnt);
597 rcnt = dlen;
598 }
599 if (rcnt == 0) {
600 *lenp = 0;
601 goto out;
602 }
603 /* paranoid */
604 if (rcnt > cnt) {
605 SMBSDEBUG("bad server! rcnt %d, cnt %d\n",
606 (int)rcnt, (int)cnt);
607 rcnt = cnt;
608 }
609 error = md_get_uio(mdp, uiop, (int)rcnt);
610 if (error)
611 goto out;
612
613 /* success */
614 *lenp = (int)rcnt;
615
616 out:
617 smb_rq_done(rqp);
618 return (error);
619 }
620
621 static int
smb_smb_write(struct smb_share * ssp,uint16_t fid,uint32_t * lenp,uio_t * uiop,smb_cred_t * scred,int timo)622 smb_smb_write(struct smb_share *ssp, uint16_t fid, uint32_t *lenp,
623 uio_t *uiop, smb_cred_t *scred, int timo)
624 {
625 struct smb_rq *rqp;
626 struct mbchain *mbp;
627 struct mdchain *mdp;
628 int error;
629 uint32_t off32;
630 uint16_t cnt, rcnt, todo;
631 uint8_t wc;
632
633 ASSERT(uiop->uio_loffset <= UINT32_MAX);
634 off32 = (uint32_t)uiop->uio_loffset;
635 ASSERT(*lenp <= UINT16_MAX);
636 cnt = (uint16_t)*lenp;
637 /* This next is an "estimate" of planned writes. */
638 todo = (uint16_t)min(uiop->uio_resid, UINT16_MAX);
639
640 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
641 if (error)
642 return (error);
643 smb_rq_getrequest(rqp, &mbp);
644 smb_rq_wstart(rqp);
645 mb_put_uint16le(mbp, fid);
646 mb_put_uint16le(mbp, cnt);
647 mb_put_uint32le(mbp, off32);
648 mb_put_uint16le(mbp, todo);
649 smb_rq_wend(rqp);
650 smb_rq_bstart(rqp);
651 mb_put_uint8(mbp, SMB_DT_DATA);
652 mb_put_uint16le(mbp, cnt);
653
654 error = mb_put_uio(mbp, uiop, *lenp);
655 if (error)
656 goto out;
657 smb_rq_bend(rqp);
658 if (timo == 0)
659 timo = smb_timo_write;
660 error = smb_rq_simple_timed(rqp, timo);
661 if (error)
662 goto out;
663 smb_rq_getreply(rqp, &mdp);
664 error = md_get_uint8(mdp, &wc);
665 if (error)
666 goto out;
667 if (wc != 1) {
668 error = EBADRPC;
669 goto out;
670 }
671 error = md_get_uint16le(mdp, &rcnt);
672 if (error)
673 goto out;
674 *lenp = rcnt;
675
676 out:
677 smb_rq_done(rqp);
678 return (error);
679 }
680
681
682 static u_int32_t smbechoes = 0;
683
684 int
smb_smb_echo(struct smb_vc * vcp,struct smb_cred * scred,int timo)685 smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo)
686 {
687 struct smb_rq *rqp;
688 struct mbchain *mbp;
689 int error;
690
691 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
692 if (error)
693 return (error);
694 mbp = &rqp->sr_rq;
695 smb_rq_wstart(rqp);
696 mb_put_uint16le(mbp, 1); /* echo count */
697 smb_rq_wend(rqp);
698 smb_rq_bstart(rqp);
699 mb_put_uint32le(mbp, atomic_inc_32_nv(&smbechoes));
700 smb_rq_bend(rqp);
701 /*
702 * Note: the IOD calls this, so
703 * this request must not wait for
704 * connection state changes, etc.
705 */
706 rqp->sr_flags |= SMBR_NORECONNECT;
707 error = smb_rq_simple_timed(rqp, timo);
708 SMBSDEBUG("%d\n", error);
709 smb_rq_done(rqp);
710 return (error);
711 }
712