proc->thread stage 4: rework the VFS and DEVICE subsystems to take thread
[dragonfly.git] / sys / netproto / smb / smb_smb.c
... / ...
CommitLineData
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 * $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.3 2003/06/25 03:56:06 dillon Exp $
34 */
35/*
36 * various SMB requests. Most of the routines merely packs data into mbufs.
37 */
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>
47
48#include <sys/iconv.h>
49
50#include <netsmb/smb.h>
51#include <netsmb/smb_subr.h>
52#include <netsmb/smb_rq.h>
53#include <netsmb/smb_conn.h>
54#include <netsmb/smb_tran.h>
55
56struct smb_dialect {
57 int d_id;
58 const char * d_name;
59};
60
61static 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}
71};
72
73#define SMB_DIALECT_MAX (sizeof(smb_dialects) / sizeof(struct smb_dialect) - 2)
74
75static int
76smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name)
77{
78 if (scred->scr_td == vcp->vc_iod->iod_td)
79 return 0;
80 SMBERROR("wrong function called(%s)\n", name);
81 return EINVAL;
82}
83
84int
85smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
86{
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;
95
96 if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 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);
113 }
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;
131 }
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;
153 }
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;
164 }
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");
171 }
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;
199 }
200 }
201 vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
202 } else { /* an old CORE protocol */
203 sp->sv_maxmux = 1;
204 }
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;
212 }
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);
223 }
224bad:
225 smb_rq_done(rqp);
226 return error;
227}
228
229int
230smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
231{
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;
239
240 vcp->vc_smbuid = SMB_UID_UNKNOWN;
241
242 if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0)
243 return EINVAL;
244
245 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp);
246 if (error)
247 return error;
248 pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
249 encpass = malloc(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 = malloc(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 = malloc(uniplen, M_SMBTEMP, M_WAITOK);
266 smb_strtouni(ntencpass, smb_vc_getpass(vcp));
267 plen--;
268
269 /*
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.
274 */
275 uniplen = 0/*-= 2*/;
276 unipp = ntencpass;
277 }
278 } else {
279 /*
280 * In the share security mode password will be used
281 * only in the tree authentication
282 */
283 pp = "";
284 plen = 1;
285 unipp = &smb_unieol;
286 uniplen = 0 /* sizeof(smb_unieol) */;
287 }
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 */
319 }
320 smb_rq_bend(rqp);
321 if (ntencpass)
322 free(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;
329 }
330 vcp->vc_smbuid = rqp->sr_rpuid;
331bad:
332 free(encpass, M_SMBTEMP);
333 free(pbuf, M_SMBTEMP);
334 smb_rq_done(rqp);
335 return error;
336}
337
338int
339smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred)
340{
341 struct smb_rq *rqp;
342 struct mbchain *mbp;
343 int error;
344
345 if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
346 return 0;
347
348 if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0)
349 return EINVAL;
350
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;
366}
367
368static char smb_any_share[] = "?????";
369
370static char *
371smb_share_typename(int stype)
372{
373 char *pp;
374
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;
392 }
393 return pp;
394}
395
396int
397smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
398{
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;
404
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 = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
418 encpass = malloc(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;
428 }
429 }
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;
456bad:
457 if (encpass)
458 free(encpass, M_SMBTEMP);
459 if (pbuf)
460 free(pbuf, M_SMBTEMP);
461 smb_rq_done(rqp);
462 return error;
463}
464
465int
466smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
467{
468 struct smb_rq *rqp;
469 struct mbchain *mbp;
470 int error;
471
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;
487}
488
489static __inline int
490smb_smb_read(struct smb_share *ssp, u_int16_t fid,
491 int *len, int *rresid, struct uio *uio, struct smb_cred *scred)
492{
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;
499
500 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
501 if (error)
502 return error;
503
504 blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
505 rlen = *len = min(blksz, *len);
506
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;
525 }
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;
534 }
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;
542}
543
544int
545smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
546 struct smb_cred *scred)
547{
548 int tsize, len, resid;
549 int error = 0;
550
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;
560 }
561 return error;
562}
563
564static __inline int
565smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
566 struct uio *uio, struct smb_cred *scred)
567{
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;
574
575 blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
576 if (blksz > 0xffff)
577 blksz = 0xffff;
578
579 resid = *len = min(blksz, *len);
580
581 error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
582 if (error)
583 return error;
584 smb_rq_getrequest(rqp, &mbp);
585 smb_rq_wstart(rqp);
586 mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
587 mb_put_uint16le(mbp, resid);
588 mb_put_uint32le(mbp, uio->uio_offset);
589 mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
590 smb_rq_wend(rqp);
591 smb_rq_bstart(rqp);
592 mb_put_uint8(mbp, SMB_DT_DATA);
593 mb_put_uint16le(mbp, resid);
594 do {
595 error = mb_put_uio(mbp, uio, resid);
596 if (error)
597 break;
598 smb_rq_bend(rqp);
599 error = smb_rq_simple(rqp);
600 if (error)
601 break;
602 smb_rq_getreply(rqp, &mdp);
603 md_get_uint8(mdp, &wc);
604 if (wc != 1) {
605 error = EBADRPC;
606 break;
607 }
608 md_get_uint16le(mdp, &resid);
609 *rresid = resid;
610 } while(0);
611 smb_rq_done(rqp);
612 return error;
613}
614
615int
616smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
617 struct smb_cred *scred)
618{
619 int error = 0, len, tsize, resid;
620 struct uio olduio;
621
622 tsize = uio->uio_resid;
623 olduio = *uio;
624 while (tsize > 0) {
625 len = tsize;
626 error = smb_smb_write(ssp, fid, &len, &resid, uio, scred);
627 if (error)
628 break;
629 if (resid < len) {
630 error = EIO;
631 break;
632 }
633 tsize -= resid;
634 }
635 if (error) {
636 /*
637 * Errors can happen on the copyin, the rpc, etc. So they
638 * imply resid is unreliable. The only safe thing is
639 * to pretend zero bytes made it. We needn't restore the
640 * iovs because callers don't depend on them in error
641 * paths - uio_resid and uio_offset are what matter.
642 */
643 *uio = olduio;
644 }
645 return error;
646}
647
648int
649smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred)
650{
651 struct smb_rq *rqp;
652 struct mbchain *mbp;
653 int error;
654
655 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
656 if (error)
657 return error;
658 mbp = &rqp->sr_rq;
659 smb_rq_wstart(rqp);
660 mb_put_uint16le(mbp, 1);
661 smb_rq_wend(rqp);
662 smb_rq_bstart(rqp);
663 mb_put_uint32le(mbp, 0);
664 smb_rq_bend(rqp);
665 error = smb_rq_simple(rqp);
666 SMBSDEBUG("%d\n", error);
667 smb_rq_done(rqp);
668 return error;
669}