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