* Move /kernel to /boot/kernel and /modules to /boot/modules.
[dragonfly.git] / sys / netproto / smb / smb_smb.c
blobe41518bc5ebc6432d0c5dd5c9c3aebd633789fee
1 /*
2 * Copyright (c) 2000-2001 Boris Popov
3 * All rights reserved.
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.
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.
32 * $FreeBSD: src/sys/netsmb/smb_smb.c,v 1.1.2.3 2002/12/14 14:44:19 fjoe Exp $
33 * $DragonFly: src/sys/netproto/smb/smb_smb.c,v 1.7 2007/08/21 17:26:47 dillon Exp $
36 * various SMB requests. Most of the routines merely packs data into mbufs.
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/proc.h>
43 #include <sys/lock.h>
44 #include <sys/sysctl.h>
45 #include <sys/socket.h>
46 #include <sys/uio.h>
48 #include <sys/iconv.h>
50 #include "smb.h"
51 #include "smb_subr.h"
52 #include "smb_rq.h"
53 #include "smb_conn.h"
54 #include "smb_tran.h"
56 struct smb_dialect {
57 int d_id;
58 const char * d_name;
61 static struct smb_dialect smb_dialects[] = {
62 {SMB_DIALECT_CORE, "PC NETWORK PROGRAM 1.0"},
63 {SMB_DIALECT_COREPLUS, "MICROSOFT NETWORKS 1.03"},
64 {SMB_DIALECT_LANMAN1_0, "MICROSOFT NETWORKS 3.0"},
65 {SMB_DIALECT_LANMAN1_0, "LANMAN1.0"},
66 {SMB_DIALECT_LANMAN2_0, "LM1.2X002"},
67 {SMB_DIALECT_LANMAN2_0, "Samba"},
68 {SMB_DIALECT_NTLM0_12, "NT LANMAN 1.0"},
69 {SMB_DIALECT_NTLM0_12, "NT LM 0.12"},
70 {-1, NULL}
73 #define SMB_DIALECT_MAX (sizeof(smb_dialects) / sizeof(struct smb_dialect) - 2)
75 static int
76 smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name)
78 if (scred->scr_td == vcp->vc_iod->iod_td)
79 return 0;
80 SMBERROR("wrong function called(%s)\n", name);
81 return EINVAL;
84 int
85 smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
87 struct smb_dialect *dp;
88 struct smb_sopt *sp = NULL;
89 struct smb_rq *rqp;
90 struct mbchain *mbp;
91 struct mdchain *mdp;
92 u_int8_t wc, stime[8], sblen;
93 u_int16_t dindex, tw, tw1, swlen, bc;
94 int error, maxqsz;
96 if (smb_smb_nomux(vcp, scred, __func__) != 0)
97 return EINVAL;
98 vcp->vc_hflags = 0;
99 vcp->vc_hflags2 = 0;
100 vcp->obj.co_flags &= ~(SMBV_ENCRYPT);
101 sp = &vcp->vc_sopt;
102 bzero(sp, sizeof(struct smb_sopt));
103 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp);
104 if (error)
105 return error;
106 smb_rq_getrequest(rqp, &mbp);
107 smb_rq_wstart(rqp);
108 smb_rq_wend(rqp);
109 smb_rq_bstart(rqp);
110 for(dp = smb_dialects; dp->d_id != -1; dp++) {
111 mb_put_uint8(mbp, SMB_DT_DIALECT);
112 smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE);
114 smb_rq_bend(rqp);
115 error = smb_rq_simple(rqp);
116 SMBSDEBUG("%d\n", error);
117 if (error)
118 goto bad;
119 smb_rq_getreply(rqp, &mdp);
120 do {
121 error = md_get_uint8(mdp, &wc);
122 if (error)
123 break;
124 error = md_get_uint16le(mdp, &dindex);
125 if (error)
126 break;
127 if (dindex > 7) {
128 SMBERROR("Don't know how to talk with server %s (%d)\n", "xxx", dindex);
129 error = EBADRPC;
130 break;
132 dp = smb_dialects + dindex;
133 sp->sv_proto = dp->d_id;
134 SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc);
135 error = EBADRPC;
136 if (dp->d_id >= SMB_DIALECT_NTLM0_12) {
137 if (wc != 17)
138 break;
139 md_get_uint8(mdp, &sp->sv_sm);
140 md_get_uint16le(mdp, &sp->sv_maxmux);
141 md_get_uint16le(mdp, &sp->sv_maxvcs);
142 md_get_uint32le(mdp, &sp->sv_maxtx);
143 md_get_uint32le(mdp, &sp->sv_maxraw);
144 md_get_uint32le(mdp, &sp->sv_skey);
145 md_get_uint32le(mdp, &sp->sv_caps);
146 md_get_mem(mdp, stime, 8, MB_MSYSTEM);
147 md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
148 md_get_uint8(mdp, &sblen);
149 if (sblen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
150 if (sblen != SMB_MAXCHALLENGELEN) {
151 SMBERROR("Unexpected length of security blob (%d)\n", sblen);
152 break;
154 error = md_get_uint16(mdp, &bc);
155 if (error)
156 break;
157 if (sp->sv_caps & SMB_CAP_EXT_SECURITY)
158 md_get_mem(mdp, NULL, 16, MB_MSYSTEM);
159 error = md_get_mem(mdp, vcp->vc_ch, sblen, MB_MSYSTEM);
160 if (error)
161 break;
162 vcp->vc_chlen = sblen;
163 vcp->obj.co_flags |= SMBV_ENCRYPT;
165 vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
166 if (dp->d_id == SMB_DIALECT_NTLM0_12 &&
167 sp->sv_maxtx < 4096 &&
168 (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) {
169 vcp->obj.co_flags |= SMBV_WIN95;
170 SMBSDEBUG("Win95 detected\n");
172 } else if (dp->d_id > SMB_DIALECT_CORE) {
173 md_get_uint16le(mdp, &tw);
174 sp->sv_sm = tw;
175 md_get_uint16le(mdp, &tw);
176 sp->sv_maxtx = tw;
177 md_get_uint16le(mdp, &sp->sv_maxmux);
178 md_get_uint16le(mdp, &sp->sv_maxvcs);
179 md_get_uint16le(mdp, &tw); /* rawmode */
180 md_get_uint32le(mdp, &sp->sv_skey);
181 if (wc == 13) { /* >= LANMAN1 */
182 md_get_uint16(mdp, &tw); /* time */
183 md_get_uint16(mdp, &tw1); /* date */
184 md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
185 md_get_uint16le(mdp, &swlen);
186 if (swlen > SMB_MAXCHALLENGELEN)
187 break;
188 md_get_uint16(mdp, NULL); /* mbz */
189 if (md_get_uint16(mdp, &bc) != 0)
190 break;
191 if (bc < swlen)
192 break;
193 if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
194 error = md_get_mem(mdp, vcp->vc_ch, swlen, MB_MSYSTEM);
195 if (error)
196 break;
197 vcp->vc_chlen = swlen;
198 vcp->obj.co_flags |= SMBV_ENCRYPT;
201 vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
202 } else { /* an old CORE protocol */
203 sp->sv_maxmux = 1;
205 error = 0;
206 } while (0);
207 if (error == 0) {
208 vcp->vc_maxvcs = sp->sv_maxvcs;
209 if (vcp->vc_maxvcs <= 1) {
210 if (vcp->vc_maxvcs == 0)
211 vcp->vc_maxvcs = 1;
213 if (sp->sv_maxtx <= 0 || sp->sv_maxtx > 0xffff)
214 sp->sv_maxtx = 1024;
215 SMB_TRAN_GETPARAM(vcp, SMBTP_SNDSZ, &maxqsz);
216 vcp->vc_txmax = min(sp->sv_maxtx, maxqsz);
217 SMBSDEBUG("TZ = %d\n", sp->sv_tz);
218 SMBSDEBUG("CAPS = %x\n", sp->sv_caps);
219 SMBSDEBUG("MAXMUX = %d\n", sp->sv_maxmux);
220 SMBSDEBUG("MAXVCS = %d\n", sp->sv_maxvcs);
221 SMBSDEBUG("MAXRAW = %d\n", sp->sv_maxraw);
222 SMBSDEBUG("MAXTX = %d\n", sp->sv_maxtx);
224 bad:
225 smb_rq_done(rqp);
226 return error;
230 smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
232 struct smb_rq *rqp;
233 struct mbchain *mbp;
234 /* u_int8_t wc;
235 u_int16_t tw, tw1;*/
236 smb_uniptr unipp, ntencpass = NULL;
237 char *pp, *up, *pbuf, *encpass;
238 int error, plen, uniplen, ulen;
240 vcp->vc_smbuid = SMB_UID_UNKNOWN;
242 if (smb_smb_nomux(vcp, scred, __func__) != 0)
243 return EINVAL;
245 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp);
246 if (error)
247 return error;
248 pbuf = kmalloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
249 encpass = kmalloc(24, M_SMBTEMP, M_WAITOK);
250 if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
251 iconv_convstr(vcp->vc_toupper, pbuf, smb_vc_getpass(vcp));
252 iconv_convstr(vcp->vc_toserver, pbuf, pbuf);
253 if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
254 uniplen = plen = 24;
255 smb_encrypt(pbuf, vcp->vc_ch, encpass);
256 ntencpass = kmalloc(uniplen, M_SMBTEMP, M_WAITOK);
257 iconv_convstr(vcp->vc_toserver, pbuf, smb_vc_getpass(vcp));
258 smb_ntencrypt(pbuf, vcp->vc_ch, (u_char*)ntencpass);
259 pp = encpass;
260 unipp = ntencpass;
261 } else {
262 plen = strlen(pbuf) + 1;
263 pp = pbuf;
264 uniplen = plen * 2;
265 ntencpass = kmalloc(uniplen, M_SMBTEMP, M_WAITOK);
266 smb_strtouni(ntencpass, smb_vc_getpass(vcp));
267 plen--;
270 * The uniplen is zeroed because Samba cannot deal
271 * with this 2nd cleartext password. This Samba
272 * "bug" is actually a workaround for problems in
273 * Microsoft clients.
275 uniplen = 0/*-= 2*/;
276 unipp = ntencpass;
278 } else {
280 * In the share security mode password will be used
281 * only in the tree authentication
283 pp = "";
284 plen = 1;
285 unipp = &smb_unieol;
286 uniplen = 0 /* sizeof(smb_unieol) */;
288 smb_rq_wstart(rqp);
289 mbp = &rqp->sr_rq;
290 up = vcp->vc_username;
291 ulen = strlen(up) + 1;
292 mb_put_uint8(mbp, 0xff);
293 mb_put_uint8(mbp, 0);
294 mb_put_uint16le(mbp, 0);
295 mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx);
296 mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux);
297 mb_put_uint16le(mbp, vcp->vc_number);
298 mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey);
299 mb_put_uint16le(mbp, plen);
300 if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) {
301 mb_put_uint32le(mbp, 0);
302 smb_rq_wend(rqp);
303 smb_rq_bstart(rqp);
304 mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
305 smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);
306 } else {
307 mb_put_uint16le(mbp, uniplen);
308 mb_put_uint32le(mbp, 0); /* reserved */
309 mb_put_uint32le(mbp, vcp->obj.co_flags & SMBV_UNICODE ?
310 SMB_CAP_UNICODE : 0);
311 smb_rq_wend(rqp);
312 smb_rq_bstart(rqp);
313 mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
314 mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM);
315 smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* AccountName */
316 smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE); /* PrimaryDomain */
317 smb_put_dstring(mbp, vcp, "FreeBSD", SMB_CS_NONE); /* Client's OS */
318 smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE); /* Client name */
320 smb_rq_bend(rqp);
321 if (ntencpass)
322 kfree(ntencpass, M_SMBTEMP);
323 error = smb_rq_simple(rqp);
324 SMBSDEBUG("%d\n", error);
325 if (error) {
326 if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess)
327 error = EAUTH;
328 goto bad;
330 vcp->vc_smbuid = rqp->sr_rpuid;
331 bad:
332 kfree(encpass, M_SMBTEMP);
333 kfree(pbuf, M_SMBTEMP);
334 smb_rq_done(rqp);
335 return error;
339 smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred)
341 struct smb_rq *rqp;
342 struct mbchain *mbp;
343 int error;
345 if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
346 return 0;
348 if (smb_smb_nomux(vcp, scred, __func__) != 0)
349 return EINVAL;
351 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp);
352 if (error)
353 return error;
354 mbp = &rqp->sr_rq;
355 smb_rq_wstart(rqp);
356 mb_put_uint8(mbp, 0xff);
357 mb_put_uint8(mbp, 0);
358 mb_put_uint16le(mbp, 0);
359 smb_rq_wend(rqp);
360 smb_rq_bstart(rqp);
361 smb_rq_bend(rqp);
362 error = smb_rq_simple(rqp);
363 SMBSDEBUG("%d\n", error);
364 smb_rq_done(rqp);
365 return error;
368 static char smb_any_share[] = "?????";
370 static char *
371 smb_share_typename(int stype)
373 char *pp;
375 switch (stype) {
376 case SMB_ST_DISK:
377 pp = "A:";
378 break;
379 case SMB_ST_PRINTER:
380 pp = smb_any_share; /* can't use LPT: here... */
381 break;
382 case SMB_ST_PIPE:
383 pp = "IPC";
384 break;
385 case SMB_ST_COMM:
386 pp = "COMM";
387 break;
388 case SMB_ST_ANY:
389 default:
390 pp = smb_any_share;
391 break;
393 return pp;
397 smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
399 struct smb_vc *vcp;
400 struct smb_rq rq, *rqp = &rq;
401 struct mbchain *mbp;
402 char *pp, *pbuf, *encpass;
403 int error, plen, caseopt;
405 ssp->ss_tid = SMB_TID_UNKNOWN;
406 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp);
407 if (error)
408 return error;
409 vcp = rqp->sr_vc;
410 caseopt = SMB_CS_NONE;
411 if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
412 plen = 1;
413 pp = "";
414 pbuf = NULL;
415 encpass = NULL;
416 } else {
417 pbuf = kmalloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
418 encpass = kmalloc(24, M_SMBTEMP, M_WAITOK);
419 iconv_convstr(vcp->vc_toupper, pbuf, smb_share_getpass(ssp));
420 iconv_convstr(vcp->vc_toserver, pbuf, pbuf);
421 if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
422 plen = 24;
423 smb_encrypt(pbuf, vcp->vc_ch, encpass);
424 pp = encpass;
425 } else {
426 plen = strlen(pbuf) + 1;
427 pp = pbuf;
430 mbp = &rqp->sr_rq;
431 smb_rq_wstart(rqp);
432 mb_put_uint8(mbp, 0xff);
433 mb_put_uint8(mbp, 0);
434 mb_put_uint16le(mbp, 0);
435 mb_put_uint16le(mbp, 0); /* Flags */
436 mb_put_uint16le(mbp, plen);
437 smb_rq_wend(rqp);
438 smb_rq_bstart(rqp);
439 mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
440 smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt);
441 pp = vcp->vc_srvname;
442 smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt);
443 smb_put_dmem(mbp, vcp, "\\", 1, caseopt);
444 pp = ssp->ss_name;
445 smb_put_dstring(mbp, vcp, pp, caseopt);
446 pp = smb_share_typename(ssp->ss_type);
447 smb_put_dstring(mbp, vcp, pp, caseopt);
448 smb_rq_bend(rqp);
449 error = smb_rq_simple(rqp);
450 SMBSDEBUG("%d\n", error);
451 if (error)
452 goto bad;
453 ssp->ss_tid = rqp->sr_rptid;
454 ssp->ss_vcgenid = vcp->vc_genid;
455 ssp->ss_flags |= SMBS_CONNECTED;
456 bad:
457 if (encpass)
458 kfree(encpass, M_SMBTEMP);
459 if (pbuf)
460 kfree(pbuf, M_SMBTEMP);
461 smb_rq_done(rqp);
462 return error;
466 smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
468 struct smb_rq *rqp;
469 struct mbchain *mbp;
470 int error;
472 if (ssp->ss_tid == SMB_TID_UNKNOWN)
473 return 0;
474 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
475 if (error)
476 return error;
477 mbp = &rqp->sr_rq;
478 smb_rq_wstart(rqp);
479 smb_rq_wend(rqp);
480 smb_rq_bstart(rqp);
481 smb_rq_bend(rqp);
482 error = smb_rq_simple(rqp);
483 SMBSDEBUG("%d\n", error);
484 smb_rq_done(rqp);
485 ssp->ss_tid = SMB_TID_UNKNOWN;
486 return error;
489 static __inline int
490 smb_smb_read(struct smb_share *ssp, u_int16_t fid,
491 int *len, int *rresid, struct uio *uio, struct smb_cred *scred)
493 struct smb_rq *rqp;
494 struct mbchain *mbp;
495 struct mdchain *mdp;
496 u_int16_t resid, bc;
497 u_int8_t wc;
498 int error, rlen, blksz;
500 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
501 if (error)
502 return error;
504 blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
505 rlen = *len = min(blksz, *len);
507 smb_rq_getrequest(rqp, &mbp);
508 smb_rq_wstart(rqp);
509 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
510 mb_put_uint16le(mbp, rlen);
511 mb_put_uint32le(mbp, uio->uio_offset);
512 mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
513 smb_rq_wend(rqp);
514 smb_rq_bstart(rqp);
515 smb_rq_bend(rqp);
516 do {
517 error = smb_rq_simple(rqp);
518 if (error)
519 break;
520 smb_rq_getreply(rqp, &mdp);
521 md_get_uint8(mdp, &wc);
522 if (wc != 5) {
523 error = EBADRPC;
524 break;
526 md_get_uint16le(mdp, &resid);
527 md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
528 md_get_uint16le(mdp, &bc);
529 md_get_uint8(mdp, NULL); /* ignore buffer type */
530 md_get_uint16le(mdp, &resid);
531 if (resid == 0) {
532 *rresid = resid;
533 break;
535 error = md_get_uio(mdp, uio, resid);
536 if (error)
537 break;
538 *rresid = resid;
539 } while(0);
540 smb_rq_done(rqp);
541 return error;
545 smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
546 struct smb_cred *scred)
548 int tsize, len, resid;
549 int error = 0;
551 tsize = uio->uio_resid;
552 while (tsize > 0) {
553 len = tsize;
554 error = smb_smb_read(ssp, fid, &len, &resid, uio, scred);
555 if (error)
556 break;
557 tsize -= resid;
558 if (resid < len)
559 break;
561 return error;
564 static __inline int
565 smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
566 struct uio *uio, struct smb_cred *scred)
568 struct smb_rq *rqp;
569 struct mbchain *mbp;
570 struct mdchain *mdp;
571 u_int16_t resid;
572 u_int8_t wc;
573 int error, blksz;
575 /* write data must be real */
576 KKASSERT(uio->uio_segflg != UIO_NOCOPY);
578 blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
579 if (blksz > 0xffff)
580 blksz = 0xffff;
582 resid = *len = min(blksz, *len);
584 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
585 if (error)
586 return error;
587 smb_rq_getrequest(rqp, &mbp);
588 smb_rq_wstart(rqp);
589 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
590 mb_put_uint16le(mbp, resid);
591 mb_put_uint32le(mbp, uio->uio_offset);
592 mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
593 smb_rq_wend(rqp);
594 smb_rq_bstart(rqp);
595 mb_put_uint8(mbp, SMB_DT_DATA);
596 mb_put_uint16le(mbp, resid);
597 do {
598 error = mb_put_uio(mbp, uio, resid);
599 if (error)
600 break;
601 smb_rq_bend(rqp);
602 error = smb_rq_simple(rqp);
603 if (error)
604 break;
605 smb_rq_getreply(rqp, &mdp);
606 md_get_uint8(mdp, &wc);
607 if (wc != 1) {
608 error = EBADRPC;
609 break;
611 md_get_uint16le(mdp, &resid);
612 *rresid = resid;
613 } while(0);
614 smb_rq_done(rqp);
615 return error;
619 smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
620 struct smb_cred *scred)
622 int error = 0, len, tsize, resid;
623 struct uio olduio;
625 tsize = uio->uio_resid;
626 olduio = *uio;
627 while (tsize > 0) {
628 len = tsize;
629 error = smb_smb_write(ssp, fid, &len, &resid, uio, scred);
630 if (error)
631 break;
632 if (resid < len) {
633 error = EIO;
634 break;
636 tsize -= resid;
638 if (error) {
640 * Errors can happen on the copyin, the rpc, etc. So they
641 * imply resid is unreliable. The only safe thing is
642 * to pretend zero bytes made it. We needn't restore the
643 * iovs because callers don't depend on them in error
644 * paths - uio_resid and uio_offset are what matter.
646 *uio = olduio;
648 return error;
652 smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred)
654 struct smb_rq *rqp;
655 struct mbchain *mbp;
656 int error;
658 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
659 if (error)
660 return error;
661 mbp = &rqp->sr_rq;
662 smb_rq_wstart(rqp);
663 mb_put_uint16le(mbp, 1);
664 smb_rq_wend(rqp);
665 smb_rq_bstart(rqp);
666 mb_put_uint32le(mbp, 0);
667 smb_rq_bend(rqp);
668 error = smb_rq_simple(rqp);
669 SMBSDEBUG("%d\n", error);
670 smb_rq_done(rqp);
671 return error;