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