AMD64 - Refactor uio_resid and size_t assumptions.
[dragonfly.git] / sys / netproto / smb / smb_smb.c
CommitLineData
984263bc
MD
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 $
8aa7625b 33 * $DragonFly: src/sys/netproto/smb/smb_smb.c,v 1.7 2007/08/21 17:26:47 dillon Exp $
984263bc
MD
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>
e54488bb 49#include <machine/limits.h>
984263bc 50
1f2de5d4
MD
51#include "smb.h"
52#include "smb_subr.h"
53#include "smb_rq.h"
54#include "smb_conn.h"
55#include "smb_tran.h"
984263bc
MD
56
57struct smb_dialect {
58 int d_id;
59 const char * d_name;
60};
61
62static 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
76static int
77smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name)
78{
dadab5e9 79 if (scred->scr_td == vcp->vc_iod->iod_td)
984263bc
MD
80 return 0;
81 SMBERROR("wrong function called(%s)\n", name);
82 return EINVAL;
83}
84
85int
86smb_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
5e2195bf 97 if (smb_smb_nomux(vcp, scred, __func__) != 0)
984263bc
MD
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 }
225bad:
226 smb_rq_done(rqp);
227 return error;
228}
229
230int
231smb_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
5e2195bf 243 if (smb_smb_nomux(vcp, scred, __func__) != 0)
984263bc
MD
244 return EINVAL;
245
246 error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp);
247 if (error)
248 return error;
efda3bd0
MD
249 pbuf = kmalloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
250 encpass = kmalloc(24, M_SMBTEMP, M_WAITOK);
984263bc
MD
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);
efda3bd0 257 ntencpass = kmalloc(uniplen, M_SMBTEMP, M_WAITOK);
984263bc
MD
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;
efda3bd0 266 ntencpass = kmalloc(uniplen, M_SMBTEMP, M_WAITOK);
984263bc
MD
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)
efda3bd0 323 kfree(ntencpass, M_SMBTEMP);
984263bc
MD
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;
332bad:
efda3bd0
MD
333 kfree(encpass, M_SMBTEMP);
334 kfree(pbuf, M_SMBTEMP);
984263bc
MD
335 smb_rq_done(rqp);
336 return error;
337}
338
339int
340smb_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
5e2195bf 349 if (smb_smb_nomux(vcp, scred, __func__) != 0)
984263bc
MD
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
369static char smb_any_share[] = "?????";
370
371static char *
372smb_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
397int
398smb_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 {
efda3bd0
MD
418 pbuf = kmalloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
419 encpass = kmalloc(24, M_SMBTEMP, M_WAITOK);
984263bc
MD
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;
457bad:
458 if (encpass)
efda3bd0 459 kfree(encpass, M_SMBTEMP);
984263bc 460 if (pbuf)
efda3bd0 461 kfree(pbuf, M_SMBTEMP);
984263bc
MD
462 smb_rq_done(rqp);
463 return error;
464}
465
466int
467smb_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
490static __inline int
491smb_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);
e54488bb 513 mb_put_uint16le(mbp, (unsigned short)szmin(uio->uio_resid, 0xffff));
984263bc
MD
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
545int
546smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
547 struct smb_cred *scred)
548{
e54488bb 549 int len, resid;
984263bc
MD
550 int error = 0;
551
e54488bb
MD
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;
984263bc
MD
557 error = smb_smb_read(ssp, fid, &len, &resid, uio, scred);
558 if (error)
559 break;
984263bc
MD
560 if (resid < len)
561 break;
562 }
563 return error;
564}
565
566static __inline int
567smb_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
8aa7625b
MD
577 /* write data must be real */
578 KKASSERT(uio->uio_segflg != UIO_NOCOPY);
579
984263bc
MD
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);
e54488bb 594 mb_put_uint16le(mbp, (unsigned short)szmin(uio->uio_resid, 0xffff));
984263bc
MD
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
620int
621smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
622 struct smb_cred *scred)
623{
e54488bb 624 int error = 0, len, resid;
984263bc
MD
625 struct uio olduio;
626
984263bc 627 olduio = *uio;
e54488bb
MD
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;
984263bc
MD
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 }
984263bc
MD
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
654int
655smb_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}