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 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 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 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 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 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 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 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 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