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