xref: /plan9/sys/src/cmd/cifs/cifs.c (revision 976d3a687802c08e9dea20e32392e7a12b1f3502)
1 #include <u.h>
2 #include <libc.h>
3 #include <fcall.h>
4 #include <thread.h>
5 #include <9p.h>
6 #include "cifs.h"
7 
8 static char magic[] = { 0xff, 'S', 'M', 'B' };
9 
10 Session *
cifsdial(char * host,char * called,char * sysname)11 cifsdial(char *host, char *called, char *sysname)
12 {
13 	int nbt, fd;
14 	char *addr;
15 	Session *s;
16 
17 	if(Debug)
18 		fprint(2, "cifsdial: host=%s called=%s sysname=%s\n", host, called, sysname);
19 
20 	if((addr = netmkaddr(host, "tcp", "cifs")) == nil)
21 		return nil;
22 
23 	nbt = 0;
24 	if((fd = dial(addr, nil, nil, nil)) == -1){
25 		nbt = 1;
26 		if((fd = nbtdial(host, called, sysname)) == -1)
27 			return nil;
28 	}
29 
30 	s = emalloc9p(sizeof(Session));
31 	memset(s, 0, sizeof(Session));
32 
33 	s->fd = fd;
34 	s->nbt = nbt;
35 	s->mtu = MTU;
36 	s->pid = getpid();
37 	s->mid = time(nil) ^ getpid();
38 	s->uid = NO_UID;
39 	s->seq = 0;
40 	s->seqrun = 0;
41 	s->secmode = SECMODE_SIGN_ENABLED;	/* hope for the best */
42 	s->flags2 = FL2_KNOWS_LONG_NAMES | FL2_HAS_LONG_NAMES | FL2_PAGEING_IO;
43 	s->macidx = -1;
44 
45 	return s;
46 }
47 
48 void
cifsclose(Session * s)49 cifsclose(Session *s)
50 {
51 	if(s->fd)
52 		close(s->fd);
53 	free(s);
54 }
55 
56 Pkt *
cifshdr(Session * s,Share * sp,int cmd)57 cifshdr(Session *s, Share *sp, int cmd)
58 {
59 	Pkt *p;
60 	int sign, tid, dfs;
61 
62 	dfs = 0;
63 	tid = NO_TID;
64 	Active = IDLE_TIME;
65 	werrstr("");
66 	sign = s->secmode & SECMODE_SIGN_ENABLED? FL2_PACKET_SIGNATURES: 0;
67 
68 	if(sp){
69 		tid = sp->tid;
70 // FIXME!		if(sp->options & SMB_SHARE_IS_IN_DFS)
71 // FIXME!			dfs = FL2_DFS;
72 	}
73 
74 	p = emalloc9p(sizeof(Pkt) + MTU);
75 	memset(p, 0, sizeof(Pkt) +MTU);
76 
77 	p->buf = (uchar *)p + sizeof(Pkt);
78 	p->s = s;
79 
80 	qlock(&s->seqlock);
81 	if(s->seqrun){
82 		p->seq = s->seq;
83 		s->seq = (s->seq + 2) % 0x10000;
84 	}
85 	qunlock(&s->seqlock);
86 
87 	nbthdr(p);
88 	pmem(p, magic, nelem(magic));
89 	p8(p, cmd);
90 	pl32(p, 0);				/* status (error) */
91 	p8(p, FL_CASELESS_NAMES | FL_CANNONICAL_NAMES); /* flags */
92 	pl16(p, s->flags2 | dfs | sign);	/* flags2 */
93 	pl16(p, (s->pid >> 16) & 0xffff);	/* PID MS bits */
94 	pl32(p, p->seq);			/* MAC / sequence number */
95 	pl32(p, 0);				/* MAC */
96 	pl16(p, 0);				/* padding */
97 
98 	pl16(p, tid);
99 	pl16(p, s->pid & 0xffff);
100 	pl16(p, s->uid);
101 	pl16(p, s->mid);
102 
103 	p->wordbase = p8(p, 0);		/* filled in by pbytes() */
104 
105 	return p;
106 }
107 
108 void
pbytes(Pkt * p)109 pbytes(Pkt *p)
110 {
111 	int n;
112 
113 	assert(p->wordbase != nil);	/* cifshdr not called */
114 	assert(p->bytebase == nil);	/* called twice */
115 
116 	n = p->pos - p->wordbase;
117 	assert(n % 2 != 0);		/* even addr */
118 	*p->wordbase = n / 2;
119 
120 	p->bytebase = pl16(p, 0);	/* filled in by cifsrpc() */
121 }
122 
123 static void
dmp(int seq,uchar * buf)124 dmp(int seq, uchar *buf)
125 {
126 	int i;
127 
128 	if(seq == 99)
129 		print("\n   ");
130 	else
131 		print("%+2d ", seq);
132 	for(i = 0; i < 8; i++)
133 		print("%02x ", buf[i] & 0xff);
134 	print("\n");
135 }
136 
137 int
cifsrpc(Pkt * p)138 cifsrpc(Pkt *p)
139 {
140 	int flags2, got, err;
141 	uint tid, uid, seq;
142 	uchar *pos;
143 	char m[nelem(magic)];
144 
145 	pos = p->pos;
146 	if(p->bytebase){
147 		p->pos = p->bytebase;
148 		pl16(p, pos - (p->bytebase + 2)); /* 2 = sizeof bytecount */
149 	}
150 	p->pos = pos;
151 
152 	if(p->s->secmode & SECMODE_SIGN_ENABLED)
153 		macsign(p, p->seq);
154 
155 	qlock(&p->s->rpclock);
156 	got = nbtrpc(p);
157 	qunlock(&p->s->rpclock);
158 	if(got == -1)
159 		return -1;
160 
161 	gmem(p, m, nelem(magic));
162 	if(memcmp(m, magic, nelem(magic)) != 0){
163 		werrstr("cifsrpc: bad magic number in packet %20ux%02ux%02ux%02ux",
164 			m[0], m[1], m[2], m[3]);
165 		return -1;
166 	}
167 
168 	g8(p);				/* cmd */
169 	err = gl32(p);			/* errcode */
170 	g8(p);				/* flags */
171 	flags2 = gl16(p);		/* flags2 */
172 	gl16(p);			/* PID MS bits */
173 	seq = gl32(p);			/* reserved */
174 	gl32(p);			/* MAC (if in use) */
175 	gl16(p);			/* Padding */
176 	tid = gl16(p);			/* TID */
177 	gl16(p);			/* PID lsbs */
178 	uid = gl16(p);			/* UID */
179 	gl16(p);			/* mid */
180 	g8(p);				/* word count */
181 
182 	if(p->s->secmode & SECMODE_SIGN_ENABLED){
183 		if(macsign(p, p->seq+1) != 0 && p->s->seqrun){
184 			werrstr("cifsrpc: invalid packet signature");
185 print("MAC signature bad\n");
186 // FIXME: for debug only			return -1;
187 		}
188 	}else{
189 		/*
190 		 * We allow the sequence number of zero as some old samba
191 		 * servers seem to fall back to this unexpectedly
192 		 * after reporting sequence numbers correctly for a while.
193 		 *
194 		 * Some other samba servers seem to always report a sequence
195 		 * number of zero if MAC signing is disabled, so we have to
196 		 * catch that too.
197 		 */
198 		if(p->s->seqrun && seq != p->seq && seq != 0){
199 			print("%ux != %ux bad sequence number\n", seq, p->seq);
200 			return -1;
201 		}
202 	}
203 
204 	p->tid = tid;
205 	if(p->s->uid == NO_UID)
206 		p->s->uid = uid;
207 
208 	if(flags2 & FL2_NT_ERRCODES){
209 		/* is it a real error rather than info/warning/chatter? */
210 		if((err & 0xF0000000) == 0xC0000000){
211 			werrstr("%s", nterrstr(err));
212 			return -1;
213 		}
214 	}else{
215 		if(err){
216 			werrstr("%s", doserrstr(err));
217 			return -1;
218 		}
219 	}
220 	return got;
221 }
222 
223 
224 /*
225  * Some older servers (old samba) prefer to talk older
226  * dialects but if given no choice they will talk the
227  * more modern ones, so we don't give them the choice.
228  */
229 int
CIFSnegotiate(Session * s,long * svrtime,char * domain,int domlen,char * cname,int cnamlen)230 CIFSnegotiate(Session *s, long *svrtime, char *domain, int domlen, char *cname,
231 	int cnamlen)
232 {
233 	int d, i;
234 	char *ispeak = "NT LM 0.12";
235 	static char *dialects[] = {
236 //		{ "PC NETWORK PROGRAM 1.0"},
237 //		{ "MICROSOFT NETWORKS 1.03"},
238 //		{ "MICROSOFT NETWORKS 3.0"},
239 //		{ "LANMAN1.0"},
240 //		{ "LM1.2X002"},
241 //		{ "NT LANMAN 1.0"},
242 		{ "NT LM 0.12" },
243 	};
244 	Pkt *p;
245 
246 	p = cifshdr(s, nil, SMB_COM_NEGOTIATE);
247 	pbytes(p);
248 	for(i = 0; i < nelem(dialects); i++){
249 		p8(p, STR_DIALECT);
250 		pstr(p, dialects[i]);
251 	}
252 
253 	if(cifsrpc(p) == -1){
254 		free(p);
255 		return -1;
256 	}
257 
258 	d = gl16(p);
259 	if(d < 0 || d > nelem(dialects)){
260 		werrstr("no CIFS dialect in common");
261 		free(p);
262 		return -1;
263 	}
264 
265 	if(strcmp(dialects[d], ispeak) != 0){
266 		werrstr("%s dialect unsupported", dialects[d]);
267 		free(p);
268 		return -1;
269 	}
270 
271 	s->secmode = g8(p);			/* Security mode */
272 
273 	gl16(p);				/* Max outstanding requests */
274 	gl16(p);				/* Max VCs */
275 	s->mtu = gl32(p);			/* Max buffer size */
276 	gl32(p);				/* Max raw buffer size (depricated) */
277 	gl32(p);				/* Session key */
278 	s->caps = gl32(p);			/* Server capabilities */
279 	*svrtime = gvtime(p);			/* fileserver time */
280 	s->tz = (short)gl16(p) * 60; /* TZ in mins, is signed (SNIA doc is wrong) */
281 	s->challen = g8(p);			/* Encryption key length */
282 	gl16(p);
283 	gmem(p, s->chal, s->challen);		/* Get the challenge */
284 	gstr(p, domain, domlen);		/* source domain */
285 
286 	{		/* NetApp Filer seem not to report its called name */
287 		char *cn = emalloc9p(cnamlen);
288 
289 		gstr(p, cn, cnamlen);		/* their name */
290 		if(strlen(cn) > 0)
291 			memcpy(cname, cn, cnamlen);
292 		free(cn);
293 	}
294 
295 	if(s->caps & CAP_UNICODE)
296 		s->flags2 |= FL2_UNICODE;
297 
298 	free(p);
299 	return 0;
300 }
301 
302 int
CIFSsession(Session * s)303 CIFSsession(Session *s)
304 {
305 	char os[64], *q;
306 	Rune r;
307 	Pkt *p;
308 	enum {
309 		mycaps = CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
310 			CAP_NT_FIND | CAP_STATUS32,
311 	};
312 
313 	s->seqrun = 1;	/* activate the sequence number generation/checking */
314 
315 	p = cifshdr(s, nil, SMB_COM_SESSION_SETUP_ANDX);
316 	p8(p, 0xFF);			/* No secondary command */
317 	p8(p, 0);			/* Reserved (must be zero) */
318 	pl16(p, 0);			/* Offset to next command */
319 	pl16(p, MTU);			/* my max buffer size */
320 	pl16(p, 1);			/* my max multiplexed pending requests */
321 	pl16(p, 0);			/* Virtual connection # */
322 	pl32(p, 0);			/* Session key (if vc != 0) */
323 
324 
325 	if((s->secmode & SECMODE_PW_ENCRYPT) == 0) {
326 		pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size */
327 		pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size (UPPER CASE) */
328 		pl32(p, 0);			/* Reserved */
329 		pl32(p, mycaps);
330 		pbytes(p);
331 
332 		for(q = Sess->auth->resp[0]; *q; ){
333 			q += chartorune(&r, q);
334 			pl16(p, toupperrune(r));
335 		}
336 		pl16(p, 0);
337 
338 		for(q = Sess->auth->resp[0]; *q; ){
339 			q += chartorune(&r, q);
340 			pl16(p, r);
341 		}
342 		pl16(p, 0);
343 	}else{
344 		pl16(p, Sess->auth->len[0]);	/* LM passwd size */
345 		pl16(p, Sess->auth->len[1]);	/* NTLM passwd size */
346 		pl32(p, 0);			/* Reserved  */
347 		pl32(p, mycaps);
348 		pbytes(p);
349 
350 		pmem(p, Sess->auth->resp[0], Sess->auth->len[0]);
351 		pmem(p, Sess->auth->resp[1], Sess->auth->len[1]);
352 	}
353 
354 	pstr(p, Sess->auth->user);	/* Account name */
355 	pstr(p, Sess->auth->windom);	/* Primary domain */
356 	pstr(p, "plan9");		/* Client OS */
357 	pstr(p, argv0);			/* Client LAN Manager type */
358 
359 	if(cifsrpc(p) == -1){
360 		free(p);
361 		return -1;
362 	}
363 
364 	g8(p);				/* Reserved (0) */
365 	gl16(p);			/* Offset to next command wordcount */
366 	Sess->isguest = gl16(p) & 1;	/* logged in as guest */
367 
368 	gl16(p);
369 	gl16(p);
370 	/* no security blob here - we don't understand extended security anyway */
371 	gstr(p, os, sizeof(os));
372 	s->remos = estrdup9p(os);
373 
374 	free(p);
375 	return 0;
376 }
377 
378 
CIFStreeconnect(Session * s,char * cname,char * tree,Share * sp)379 CIFStreeconnect(Session *s, char *cname, char *tree, Share *sp)
380 {
381 	int len;
382 	char *resp, *path;
383 	char zeros[24];
384 	Pkt *p;
385 
386 	resp = Sess->auth->resp[0];
387 	len  = Sess->auth->len[0];
388 	if((s->secmode & SECMODE_USER) != SECMODE_USER){
389 		memset(zeros, 0, sizeof(zeros));
390 		resp = zeros;
391 		len = sizeof(zeros);
392 	}
393 
394 	p = cifshdr(s, nil, SMB_COM_TREE_CONNECT_ANDX);
395 	p8(p, 0xFF);			/* Secondary command */
396 	p8(p, 0);			/* Reserved */
397 	pl16(p, 0);			/* Offset to next Word Count */
398 	pl16(p, 0);			/* Flags */
399 
400 	if((s->secmode & SECMODE_PW_ENCRYPT) == 0){
401 		pl16(p, len+1);		/* password len, including null */
402 		pbytes(p);
403 		pascii(p, resp);
404 	}else{
405 		pl16(p, len);
406 		pbytes(p);
407 		pmem(p, resp, len);
408 	}
409 
410 	path = smprint("//%s/%s", cname, tree);
411 	strupr(path);
412 	ppath(p, path);			/* path */
413 	free(path);
414 
415 	pascii(p, "?????");	/* service type any (so we can do RAP calls) */
416 
417 	if(cifsrpc(p) == -1){
418 		free(p);
419 		return -1;
420 	}
421 	g8(p);				/* Secondary command */
422 	g8(p);				/* Reserved */
423 	gl16(p);			/* Offset to next command */
424 	sp->options = g8(p);		/* options supported */
425 	sp->tid = p->tid;		/* get received TID from packet header */
426 	free(p);
427 	return 0;
428 }
429 
430 int
CIFSlogoff(Session * s)431 CIFSlogoff(Session *s)
432 {
433 	int rc;
434 	Pkt *p;
435 
436 	p = cifshdr(s, nil, SMB_COM_LOGOFF_ANDX);
437 	p8(p, 0xFF);			/* No ANDX command */
438 	p8(p, 0);			/* Reserved (must be zero) */
439 	pl16(p, 0);			/* offset ot ANDX */
440 	pbytes(p);
441 	rc = cifsrpc(p);
442 
443 	free(p);
444 	return rc;
445 }
446 
447 int
CIFStreedisconnect(Session * s,Share * sp)448 CIFStreedisconnect(Session *s, Share *sp)
449 {
450 	int rc;
451 	Pkt *p;
452 
453 	p = cifshdr(s, sp, SMB_COM_TREE_DISCONNECT);
454 	pbytes(p);
455 	rc = cifsrpc(p);
456 
457 	free(p);
458 	return rc;
459 }
460 
461 
462 int
CIFSdeletefile(Session * s,Share * sp,char * name)463 CIFSdeletefile(Session *s, Share *sp, char *name)
464 {
465 	int rc;
466 	Pkt *p;
467 
468 	p = cifshdr(s, sp, SMB_COM_DELETE);
469 	pl16(p, ATTR_HIDDEN|ATTR_SYSTEM);	/* search attributes */
470 	pbytes(p);
471 	p8(p, STR_ASCII);			/* buffer format */
472 	ppath(p, name);
473 	rc = cifsrpc(p);
474 
475 	free(p);
476 	return rc;
477 }
478 
479 int
CIFSdeletedirectory(Session * s,Share * sp,char * name)480 CIFSdeletedirectory(Session *s, Share *sp, char *name)
481 {
482 	int rc;
483 	Pkt *p;
484 
485 	p = cifshdr(s, sp, SMB_COM_DELETE_DIRECTORY);
486 	pbytes(p);
487 	p8(p, STR_ASCII);		/* buffer format */
488 	ppath(p, name);
489 	rc = cifsrpc(p);
490 
491 	free(p);
492 	return rc;
493 }
494 
495 int
CIFScreatedirectory(Session * s,Share * sp,char * name)496 CIFScreatedirectory(Session *s, Share *sp, char *name)
497 {
498 	int rc;
499 	Pkt *p;
500 
501 	p = cifshdr(s, sp, SMB_COM_CREATE_DIRECTORY);
502 	pbytes(p);
503 	p8(p, STR_ASCII);
504 	ppath(p, name);
505 	rc = cifsrpc(p);
506 
507 	free(p);
508 	return rc;
509 }
510 
511 int
CIFSrename(Session * s,Share * sp,char * old,char * new)512 CIFSrename(Session *s, Share *sp, char *old, char *new)
513 {
514 	int rc;
515 	Pkt *p;
516 
517 	p = cifshdr(s, sp, SMB_COM_RENAME);
518 	pl16(p, ATTR_HIDDEN|ATTR_SYSTEM|ATTR_DIRECTORY); /* search attributes */
519 	pbytes(p);
520 	p8(p, STR_ASCII);
521 	ppath(p, old);
522 	p8(p, STR_ASCII);
523 	ppath(p, new);
524 	rc = cifsrpc(p);
525 
526 	free(p);
527 	return rc;
528 }
529 
530 
531 /* for NT4/Win2k/XP */
532 int
CIFS_NT_opencreate(Session * s,Share * sp,char * name,int flags,int options,int attrs,int access,int share,int action,int * result,FInfo * fi)533 CIFS_NT_opencreate(Session *s, Share *sp, char *name, int flags, int options,
534 	int attrs, int access, int share, int action, int *result, FInfo *fi)
535 {
536 	Pkt *p;
537 	int fh;
538 
539 	p = cifshdr(s, sp, SMB_COM_NT_CREATE_ANDX);
540 	p8(p, 0xFF);			/* Secondary command */
541 	p8(p, 0);			/* Reserved */
542 	pl16(p, 0);			/* Offset to next command */
543 	p8(p, 0);			/* Reserved */
544 	pl16(p, utflen(name) *2);	/* file name len */
545 	pl32(p, flags);			/* Flags */
546 	pl32(p, 0);			/* fid of cwd, if relative path */
547 	pl32(p, access);		/* access desired */
548 	pl64(p, 0);			/* initial allocation size */
549 	pl32(p, attrs);			/* Extended attributes */
550 	pl32(p, share);			/* Share Access */
551 	pl32(p, action);		/* What to do on success/failure */
552 	pl32(p, options);		/* Options */
553 	pl32(p, SECURITY_IMPERSONATION); /* Impersonation level */
554 	p8(p, SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY); /* security flags */
555 	pbytes(p);
556 	p8(p, 0);			/* FIXME: padding? */
557 	ppath(p, name);			/* filename */
558 
559 	if(cifsrpc(p) == -1){
560 		free(p);
561 		return -1;
562 	}
563 
564 	memset(fi, 0, sizeof(FInfo));
565 	g8(p);				/* Secondary command */
566 	g8(p);				/* Reserved */
567 	gl16(p);			/* Offset to next command */
568 	g8(p);				/* oplock granted */
569 	fh = gl16(p);			/* FID for opened object */
570 	*result = gl32(p);		/* create action taken */
571 	gl64(p);			/* creation time */
572 	fi->accessed = gvtime(p);	/* last access time */
573 	fi->written = gvtime(p);	/* last written time */
574 	fi->changed = gvtime(p);	/* change time */
575 	fi->attribs = gl32(p);		/* extended attributes */
576 	gl64(p);			/* bytes allocated */
577 	fi->size = gl64(p);		/* file size */
578 
579 	free(p);
580 	return fh;
581 }
582 
583 /* for Win95/98/ME */
CIFS_SMB_opencreate(Session * s,Share * sp,char * name,int access,int attrs,int action,int * result)584 CIFS_SMB_opencreate(Session *s, Share *sp, char *name, int access,
585 	int attrs, int action, int *result)
586 {
587 	Pkt *p;
588 	int fh;
589 
590 	p = cifshdr(s, sp, SMB_COM_OPEN_ANDX);
591 	p8(p, 0xFF);			/* Secondary command */
592 	p8(p, 0);			/* Reserved */
593 	pl16(p, 0);			/* Offset to next command */
594 	pl16(p, 0);			/* Flags (0 == no stat(2) info) */
595 	pl16(p, access);		/* desired access */
596 	pl16(p, ATTR_HIDDEN|ATTR_SYSTEM);/* search attributes */
597 	pl16(p, attrs);			/* file attribytes */
598 	pdatetime(p, 0);		/* creation time (0 == now) */
599 	pl16(p, action);		/* What to do on success/failure */
600 	pl32(p, 0);			/* allocation size */
601 	pl32(p, 0);			/* reserved */
602 	pl32(p, 0);			/* reserved */
603 	pbytes(p);
604 	ppath(p, name);			/* filename */
605 
606 	if(cifsrpc(p) == -1){
607 		free(p);
608 		return -1;
609 	}
610 
611 	g8(p);				/* Secondary command */
612 	g8(p);				/* Reserved */
613 	gl16(p);			/* Offset to next command */
614 	fh = gl16(p);			/* FID for opened object */
615 	gl16(p);			/* extended attributes */
616 	gvtime(p);			/* last written time */
617 	gl32(p);			/* file size */
618 	gl16(p);			/* file type (disk/fifo/printer etc) */
619 	gl16(p);			/* device status (for fifos) */
620 	*result = gl16(p);		/* access granted */
621 
622 	free(p);
623 	return fh;
624 }
625 
626 vlong
CIFSwrite(Session * s,Share * sp,int fh,uvlong off,void * buf,vlong n)627 CIFSwrite(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n)
628 {
629 	Pkt *p;
630 	vlong got;
631 
632 	/* FIXME: Payload should be padded to long boundary */
633 	assert((n   & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
634 	assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
635 	assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_WRITEX);
636 
637 	p = cifshdr(s, sp, SMB_COM_WRITE_ANDX);
638 	p8(p, 0xFF);			/* Secondary command */
639 	p8(p, 0);			/* Reserved */
640 	pl16(p, 0);			/* Offset to next command */
641 	pl16(p, fh);			/* File handle */
642 	pl32(p, off & 0xffffffff);	/* LSBs of Offset */
643 	pl32(p, 0);			/* Reserved (0) */
644 	pl16(p, s->nocache);		/* Write mode (0 - write through) */
645 	pl16(p, 0);			/* Bytes remaining */
646 	pl16(p, n >> 16);		/* MSBs of length */
647 	pl16(p, n & 0xffffffff);	/* LSBs of length */
648 	pl16(p, T2HDRLEN);		/* Offset to data, in bytes */
649 	pl32(p, off >> 32);		/* MSBs of offset */
650 	pbytes(p);
651 
652 	p->pos = p->buf +T2HDRLEN +NBHDRLEN;
653 	pmem(p, buf, n);		/* Data */
654 
655 	if(cifsrpc(p) == -1){
656 		free(p);
657 		return -1;
658 	}
659 
660 	g8(p);				/* Secondary command */
661 	g8(p);				/* Reserved */
662 	gl16(p);			/* Offset to next command */
663 	got = gl16(p);			/* LSWs of bytes written */
664 	gl16(p);			/* remaining (space ?) */
665 	got |= (gl16(p) << 16);		/* MSWs of bytes written */
666 
667 	free(p);
668 	return got;
669 }
670 
671 vlong
CIFSread(Session * s,Share * sp,int fh,uvlong off,void * buf,vlong n,vlong minlen)672 CIFSread(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n,
673 	vlong minlen)
674 {
675 	int doff;
676 	vlong got;
677 	Pkt *p;
678 
679 	assert((n   & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
680 	assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
681 	assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_READX);
682 
683 	p = cifshdr(s, sp, SMB_COM_READ_ANDX);
684 	p8(p, 0xFF);			/* Secondary command */
685 	p8(p, 0);			/* Reserved */
686 	pl16(p, 0);			/* Offset to next command */
687 	pl16(p, fh);			/* File handle */
688 	pl32(p, off & 0xffffffff);	/* Offset to beginning of write */
689 	pl16(p, n);			/* Maximum number of bytes to return */
690 	pl16(p, minlen);		/* Minimum number of bytes to return */
691 	pl32(p, (uint)n >> 16);		/* MSBs of maxlen */
692 	pl16(p, 0);			/* Bytes remaining to satisfy request */
693 	pl32(p, off >> 32);		/* MS 32 bits of offset */
694 	pbytes(p);
695 
696 	if(cifsrpc(p) == -1){
697 		free(p);
698 		return -1;
699 	}
700 
701 	g8(p);				/* Secondary command */
702 	g8(p);				/* Reserved */
703 	gl16(p);			/* Offset to next command */
704 	gl16(p);			/* Remaining */
705 	gl16(p);			/* Compression mode */
706 	gl16(p);			/* Reserved */
707 	got = gl16(p);			/* length */
708 	doff = gl16(p);			/* Offset from header to data */
709 	got |= gl16(p) << 16;
710 
711 	p->pos = p->buf + doff + NBHDRLEN;
712 
713 	gmem(p, buf, got);		 /* data */
714 	free(p);
715 	return got;
716 }
717 
718 int
CIFSflush(Session * s,Share * sp,int fh)719 CIFSflush(Session *s, Share *sp, int fh)
720 {
721 	int rc;
722 	Pkt *p;
723 
724 	p = cifshdr(s, sp, SMB_COM_FLUSH);
725 	pl16(p, fh);			/* fid */
726 	pbytes(p);
727 	rc = cifsrpc(p);
728 
729 	free(p);
730 	return rc;
731 }
732 
733 /*
734  * Setting the time of last write to -1 gives "now" if the file
735  * was written and leaves it the same if the file wasn't written.
736  */
737 int
CIFSclose(Session * s,Share * sp,int fh)738 CIFSclose(Session *s, Share *sp, int fh)
739 {
740 	int rc;
741 	Pkt *p;
742 
743 	p = cifshdr(s, sp, SMB_COM_CLOSE);
744 	pl16(p, fh);			/* fid */
745 	pl32(p, ~0L);			/* Time of last write (none) */
746 	pbytes(p);
747 	rc = cifsrpc(p);
748 
749 	free(p);
750 	return rc;
751 }
752 
753 
754 int
CIFSfindclose2(Session * s,Share * sp,int sh)755 CIFSfindclose2(Session *s, Share *sp, int sh)
756 {
757 	int rc;
758 	Pkt *p;
759 
760 	p = cifshdr(s, sp, SMB_COM_FIND_CLOSE2);
761 	pl16(p, sh);			/* sid */
762 	pbytes(p);
763 	rc = cifsrpc(p);
764 
765 	free(p);
766 	return rc;
767 }
768 
769 
770 int
CIFSecho(Session * s)771 CIFSecho(Session *s)
772 {
773 	Pkt *p;
774 	int rc;
775 
776 	p = cifshdr(s, nil, SMB_COM_ECHO);
777 	pl16(p, 1);				/* number of replies */
778 	pbytes(p);
779 	pascii(p, "abcdefghijklmnopqrstuvwxyz"); /* data */
780 
781 	rc = cifsrpc(p);
782 	free(p);
783 	return rc;
784 }
785 
786 
787 int
CIFSsetinfo(Session * s,Share * sp,char * path,FInfo * fip)788 CIFSsetinfo(Session *s, Share *sp, char *path, FInfo *fip)
789 {
790 	int rc;
791 	Pkt *p;
792 
793 	p = cifshdr(s, sp, SMB_COM_SET_INFORMATION);
794 	pl16(p, fip->attribs);
795 	pl32(p, time(nil) - s->tz);	/* modified time */
796 	pl64(p, 0);			/* reserved */
797 	pl16(p, 0);			/* reserved */
798 
799 	pbytes(p);
800 	p8(p, STR_ASCII);		/* buffer format */
801 	ppath(p, path);
802 
803 	rc = cifsrpc(p);
804 	free(p);
805 	return rc;
806 }
807