]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cifs/cifs.c
grep: error if sbrk fails
[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 | FL2_UNICODE;
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[] = { { "NT LM 0.12" } };
251         Pkt *p;
252
253         p = cifshdr(s, nil, SMB_COM_NEGOTIATE);
254
255         pbytes(p);
256         for(i = 0; i < nelem(dialects); i++){
257                 p8(p, STR_DIALECT);
258                 pascii(p, dialects[i]);
259         }
260
261         if(cifsrpc(p) == -1){
262                 free(p);
263                 return -1;
264         }
265
266         d = gl16(p);
267         if(d < 0 || d > nelem(dialects)){
268                 werrstr("no CIFS dialect in common");
269                 free(p);
270                 return -1;
271         }
272
273         if(strcmp(dialects[d], ispeak) != 0){
274                 werrstr("%s dialect unsupported", dialects[d]);
275                 free(p);
276                 return -1;
277         }
278
279         s->secmode = g8(p);                             /* Security mode */
280
281         gl16(p);                                                /* Max outstanding requests */
282         gl16(p);                                                /* Max VCs */
283         s->mtu = gl32(p);                               /* Max buffer size */
284         gl32(p);                                                /* Max raw buffer size (depricated) */
285         gl32(p);                                                /* Session key */
286         s->caps = gl32(p);                              /* Server capabilities */
287         *svrtime = gvtime(p);                   /* fileserver time */
288         s->tz = (short)gl16(p) * 60;    /* TZ in mins, is signed (SNIA doc is wrong) */
289         s->challen = g8(p);                             /* Encryption key length */
290         gl16(p);
291         gmem(p, s->chal, s->challen);   /* Get the challenge */
292
293         /*
294          * for some weird reason the following two string always seem to be in unicode,
295          * however they are NOT byte aligned, every other packet is correctly aligned
296          */
297         gstr_noalign(p, domain, domlen);                /* source domain */
298         {       /* NetApp Filer seem not to report its called name */
299                 char *cn = emalloc9p(cnamlen);
300                 gstr_noalign(p, cn, cnamlen);           /* their name */
301                 if(strlen(cn) > 0)
302                         memcpy(cname, cn, cnamlen);
303                 free(cn);
304         }
305
306         if(s->caps & CAP_UNICODE)
307                 s->flags2 |= FL2_UNICODE;
308         else
309                 s->flags2 &= ~FL2_UNICODE;
310
311         free(p);
312         return 0;
313 }
314
315 int
316 CIFSsession(Session *s)
317 {
318         char os[64], *q;
319         Rune r;
320         Pkt *p;
321         enum {
322                 mycaps = CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
323                         CAP_NT_FIND | CAP_STATUS32,
324         };
325
326         s->seqrun = 1;  /* activate the sequence number generation/checking */
327
328         p = cifshdr(s, nil, SMB_COM_SESSION_SETUP_ANDX);
329         p8(p, 0xFF);                            /* No secondary command */
330         p8(p, 0);                                       /* Reserved (must be zero) */
331         pl16(p, 0);                                     /* Offset to next command */
332         pl16(p, MTU);                           /* my max buffer size */
333         pl16(p, 1);                                     /* my max multiplexed pending requests */
334         pl16(p, 0);                                     /* Virtual connection # */
335         pl32(p, 0);                                     /* Session key (if vc != 0) */
336
337         if(Debug && strstr(Debug, "auth") != nil)
338                 fprint(2, "mycaps=%x\n", mycaps);
339
340         if((s->secmode & SECMODE_PW_ENCRYPT) == 0) {
341                 if(Debug && strstr(Debug, "auth") != nil)
342                         fprint(2, "user=%d %q\npass=%d %q\n", Sess->auth->len[0], Sess->auth->resp[0], Sess->auth->len[1], Sess->auth->resp[1]);
343
344                 pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size */
345                 pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size (UPPER CASE) */
346                 pl32(p, 0);                             /* Reserved */
347                 pl32(p, mycaps);
348                 pbytes(p);
349
350                 for(q = Sess->auth->resp[0]; *q; ){
351                         q += chartorune(&r, q);
352                         if(r > Bits16)
353                                 sysfatal("CIFSsession: '%C' utf too wide for windows\n", r);
354                         pl16(p, toupperrune(r));
355                 }
356                 pl16(p, 0);
357
358                 for(q = Sess->auth->resp[0]; *q; ){
359                         q += chartorune(&r, q);
360                         if(r > Bits16)
361                                 sysfatal("CIFSsession: '%C' utf too wide for windows\n", r);
362                         pl16(p, r);
363                 }
364                 pl16(p, 0);
365         }else{
366                 if(Debug && strstr(Debug, "auth") != nil)
367                         fprint(2, "encrypted len=%d,%d\n", Sess->auth->len[0], Sess->auth->len[1]);
368
369                 pl16(p, Sess->auth->len[0]);    /* LM passwd size */
370                 pl16(p, Sess->auth->len[1]);    /* NTLM passwd size */
371                 pl32(p, 0);                                             /* Reserved  */
372                 pl32(p, mycaps);
373                 pbytes(p);
374
375                 pmem(p, Sess->auth->resp[0], Sess->auth->len[0]);
376                 pmem(p, Sess->auth->resp[1], Sess->auth->len[1]);
377         }
378
379         if(Debug && strstr(Debug, "auth") != nil)
380                 fprint(2, "user=%q\nwindom=%q\nos=%s\nmanager=%s\n", Sess->auth->user, Sess->auth->windom, "plan9", argv0);
381
382         pstr(p, Sess->auth->user);              /* Account name */
383         pstr(p, Sess->auth->windom);    /* Primary domain */
384         pstr(p, "plan9");                               /* Client OS */
385         pstr(p, argv0);                                 /* Client LAN Manager type */
386
387         if(cifsrpc(p) == -1){
388                 free(p);
389                 return -1;
390         }
391
392         g8(p);                                                  /* Reserved (0) */
393         gl16(p);                                                /* Offset to next command wordcount */
394         Sess->isguest = gl16(p) & 1;    /* logged in as guest */
395
396         gl16(p);
397         gl16(p);
398         /* no security blob here - we don't understand extended security anyway */
399         gstr(p, os, sizeof os);
400         s->remos = estrdup9p(os);
401
402         free(p);
403         return 0;
404 }
405
406
407 CIFStreeconnect(Session *s, char *cname, char *tree, Share *sp)
408 {
409         int len;
410         char *resp, *path;
411         char zeros[24];
412         Pkt *p;
413
414         resp = Sess->auth->resp[0];
415         len  = Sess->auth->len[0];
416         if((s->secmode & SECMODE_USER) != SECMODE_USER){
417                 memset(zeros, 0, sizeof zeros);
418                 resp = zeros;
419                 len = sizeof zeros;
420         }
421
422         p = cifshdr(s, nil, SMB_COM_TREE_CONNECT_ANDX);
423         p8(p, 0xFF);                    /* Secondary command */
424         p8(p, 0);                       /* Reserved */
425         pl16(p, 0);                     /* Offset to next Word Count */
426         pl16(p, 0);                     /* Flags */
427
428         if((s->secmode & SECMODE_PW_ENCRYPT) == 0){
429                 pl16(p, len+1);         /* password len, including null */
430                 pbytes(p);
431                 pascii(p, resp);
432         }else{
433                 pl16(p, len);
434                 pbytes(p);
435                 pmem(p, resp, len);
436         }
437
438         path = smprint("//%s/%s", cname, tree);
439
440         ppath(p, path);                 /* path */
441         free(path);
442
443         pascii(p, "?????");             /* service type any (so we can do RAP calls) */
444
445         if(cifsrpc(p) == -1){
446                 free(p);
447                 return -1;
448         }
449         g8(p);                          /* Secondary command */
450         g8(p);                          /* Reserved */
451         gl16(p);                        /* Offset to next command */
452         sp->options = g8(p);            /* options supported */
453         sp->tid = p->tid;               /* get received TID from packet header */
454         free(p);
455         return 0;
456 }
457
458 int
459 CIFSlogoff(Session *s)
460 {
461         int rc;
462         Pkt *p;
463
464         p = cifshdr(s, nil, SMB_COM_LOGOFF_ANDX);
465         p8(p, 0xFF);                    /* No ANDX command */
466         p8(p, 0);                       /* Reserved (must be zero) */
467         pl16(p, 0);                     /* offset ot ANDX */
468         pbytes(p);
469         rc = cifsrpc(p);
470
471         free(p);
472         return rc;
473 }
474
475 int
476 CIFStreedisconnect(Session *s, Share *sp)
477 {
478         int rc;
479         Pkt *p;
480
481         p = cifshdr(s, sp, SMB_COM_TREE_DISCONNECT);
482         pbytes(p);
483         rc = cifsrpc(p);
484
485         free(p);
486         return rc;
487 }
488
489
490 int
491 CIFSdeletefile(Session *s, Share *sp, char *name)
492 {
493         int rc;
494         Pkt *p;
495
496         p = cifshdr(s, sp, SMB_COM_DELETE);
497         pl16(p, ATTR_HIDDEN|ATTR_SYSTEM);       /* search attributes */
498         pbytes(p);
499         p8(p, STR_ASCII);                       /* buffer format */
500         ppath(p, name);
501         rc = cifsrpc(p);
502
503         free(p);
504         return rc;
505 }
506
507 int
508 CIFSdeletedirectory(Session *s, Share *sp, char *name)
509 {
510         int rc;
511         Pkt *p;
512
513         p = cifshdr(s, sp, SMB_COM_DELETE_DIRECTORY);
514         pbytes(p);
515         p8(p, STR_ASCII);               /* buffer format */
516         ppath(p, name);
517         rc = cifsrpc(p);
518
519         free(p);
520         return rc;
521 }
522
523 int
524 CIFScreatedirectory(Session *s, Share *sp, char *name)
525 {
526         int rc;
527         Pkt *p;
528
529         p = cifshdr(s, sp, SMB_COM_CREATE_DIRECTORY);
530         pbytes(p);
531         p8(p, STR_ASCII);
532         ppath(p, name);
533         rc = cifsrpc(p);
534
535         free(p);
536         return rc;
537 }
538
539 int
540 CIFSrename(Session *s, Share *sp, char *old, char *new)
541 {
542         int rc;
543         Pkt *p;
544
545         p = cifshdr(s, sp, SMB_COM_RENAME);
546         pl16(p, ATTR_HIDDEN|ATTR_SYSTEM|ATTR_DIRECTORY); /* search attributes */
547         pbytes(p);
548         p8(p, STR_ASCII);
549         ppath(p, old);
550         p8(p, STR_ASCII);
551         ppath(p, new);
552         rc = cifsrpc(p);
553
554         free(p);
555         return rc;
556 }
557
558
559 /* for NT4/Win2k/XP */
560 int
561 CIFS_NT_opencreate(Session *s, Share *sp, char *name, int flags, int options,
562         int attrs, int access, int share, int action, int *result, FInfo *fi)
563 {
564         Pkt *p;
565         int fh;
566
567         p = cifshdr(s, sp, SMB_COM_NT_CREATE_ANDX);
568         p8(p, 0xFF);                    /* Secondary command */
569         p8(p, 0);                       /* Reserved */
570         pl16(p, 0);                     /* Offset to next command */
571         p8(p, 0);                       /* Reserved */
572         pl16(p, utflen(name) *2);       /* file name len */
573         pl32(p, flags);                 /* Flags */
574         pl32(p, 0);                     /* fid of cwd, if relative path */
575         pl32(p, access);                /* access desired */
576         pl64(p, 0);                     /* initial allocation size */
577         pl32(p, attrs);                 /* Extended attributes */
578         pl32(p, share);                 /* Share Access */
579         pl32(p, action);                /* What to do on success/failure */
580         pl32(p, options);               /* Options */
581         pl32(p, SECURITY_IMPERSONATION); /* Impersonation level */
582         p8(p, SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY); /* security flags */
583         pbytes(p);
584         p8(p, 0);                       /* FIXME: padding? */
585         ppath(p, name);                 /* filename */
586
587         if(cifsrpc(p) == -1){
588                 free(p);
589                 return -1;
590         }
591
592         memset(fi, 0, sizeof(FInfo));
593         g8(p);                          /* Secondary command */
594         g8(p);                          /* Reserved */
595         gl16(p);                        /* Offset to next command */
596         g8(p);                          /* oplock granted */
597         fh = gl16(p);                   /* FID for opened object */
598         *result = gl32(p);              /* create action taken */
599         gl64(p);                        /* creation time */
600         fi->accessed = gvtime(p);       /* last access time */
601         fi->written = gvtime(p);        /* last written time */
602         fi->changed = gvtime(p);        /* change time */
603         fi->attribs = gl32(p);          /* extended attributes */
604         gl64(p);                        /* bytes allocated */
605         fi->size = gl64(p);             /* file size */
606
607         free(p);
608         return fh;
609 }
610
611 /* for Win95/98/ME */
612 CIFS_SMB_opencreate(Session *s, Share *sp, char *name, int access,
613         int attrs, int action, int *result)
614 {
615         Pkt *p;
616         int fh;
617
618         p = cifshdr(s, sp, SMB_COM_OPEN_ANDX);
619         p8(p, 0xFF);                    /* Secondary command */
620         p8(p, 0);                       /* Reserved */
621         pl16(p, 0);                     /* Offset to next command */
622         pl16(p, 0);                     /* Flags (0 == no stat(2) info) */
623         pl16(p, access);                /* desired access */
624         pl16(p, ATTR_HIDDEN|ATTR_SYSTEM);/* search attributes */
625         pl16(p, attrs);                 /* file attribytes */
626         pdatetime(p, 0);                /* creation time (0 == now) */
627         pl16(p, action);                /* What to do on success/failure */
628         pl32(p, 0);                     /* allocation size */
629         pl32(p, 0);                     /* reserved */
630         pl32(p, 0);                     /* reserved */
631         pbytes(p);
632         ppath(p, name);                 /* filename */
633
634         if(cifsrpc(p) == -1){
635                 free(p);
636                 return -1;
637         }
638
639         g8(p);                          /* Secondary command */
640         g8(p);                          /* Reserved */
641         gl16(p);                        /* Offset to next command */
642         fh = gl16(p);                   /* FID for opened object */
643         gl16(p);                        /* extended attributes */
644         gvtime(p);                      /* last written time */
645         gl32(p);                        /* file size */
646         gl16(p);                        /* file type (disk/fifo/printer etc) */
647         gl16(p);                        /* device status (for fifos) */
648         *result = gl16(p);              /* access granted */
649
650         free(p);
651         return fh;
652 }
653
654 vlong
655 CIFSwrite(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n)
656 {
657         Pkt *p;
658         vlong got;
659
660         /* FIXME: Payload should be padded to long boundary */
661         assert((n   & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
662         assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
663         assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_WRITEX);
664
665         p = cifshdr(s, sp, SMB_COM_WRITE_ANDX);
666         p8(p, 0xFF);                    /* Secondary command */
667         p8(p, 0);                       /* Reserved */
668         pl16(p, 0);                     /* Offset to next command */
669         pl16(p, fh);                    /* File handle */
670         pl32(p, off & 0xffffffff);      /* LSBs of Offset */
671         pl32(p, 0);                     /* Reserved (0) */
672         pl16(p, s->nocache);            /* Write mode (0 - write through) */
673         pl16(p, 0);                     /* Bytes remaining */
674         pl16(p, n >> 16);               /* MSBs of length */
675         pl16(p, n & 0xffffffff);        /* LSBs of length */
676         pl16(p, T2HDRLEN);              /* Offset to data, in bytes */
677         pl32(p, off >> 32);             /* MSBs of offset */
678         pbytes(p);
679
680         p->pos = p->buf +T2HDRLEN +NBHDRLEN;
681         pmem(p, buf, n);                /* Data */
682
683         if(cifsrpc(p) == -1){
684                 free(p);
685                 return -1;
686         }
687
688         g8(p);                          /* Secondary command */
689         g8(p);                          /* Reserved */
690         gl16(p);                        /* Offset to next command */
691         got = gl16(p);                  /* LSWs of bytes written */
692         gl16(p);                        /* remaining (space ?) */
693         got |= (gl16(p) << 16);         /* MSWs of bytes written */
694
695         free(p);
696         return got;
697 }
698
699 vlong
700 CIFSread(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n,
701         vlong minlen)
702 {
703         int doff;
704         vlong got;
705         Pkt *p;
706
707         assert((n   & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
708         assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
709         assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_READX);
710
711         p = cifshdr(s, sp, SMB_COM_READ_ANDX);
712         p8(p, 0xFF);                    /* Secondary command */
713         p8(p, 0);                       /* Reserved */
714         pl16(p, 0);                     /* Offset to next command */
715         pl16(p, fh);                    /* File handle */
716         pl32(p, off & 0xffffffff);      /* Offset to beginning of write */
717         pl16(p, n);                     /* Maximum number of bytes to return */
718         pl16(p, minlen);                /* Minimum number of bytes to return */
719         pl32(p, (uint)n >> 16);         /* MSBs of maxlen */
720         pl16(p, 0);                     /* Bytes remaining to satisfy request */
721         pl32(p, off >> 32);             /* MS 32 bits of offset */
722         pbytes(p);
723
724         if(cifsrpc(p) == -1){
725                 free(p);
726                 return -1;
727         }
728
729         g8(p);                          /* Secondary command */
730         g8(p);                          /* Reserved */
731         gl16(p);                        /* Offset to next command */
732         gl16(p);                        /* Remaining */
733         gl16(p);                        /* Compression mode */
734         gl16(p);                        /* Reserved */
735         got = gl16(p);                  /* length */
736         doff = gl16(p);                 /* Offset from header to data */
737         got |= gl16(p) << 16;
738
739         p->pos = p->buf + doff + NBHDRLEN;
740
741         gmem(p, buf, got);               /* data */
742         free(p);
743         return got;
744 }
745
746 int
747 CIFSflush(Session *s, Share *sp, int fh)
748 {
749         int rc;
750         Pkt *p;
751
752         p = cifshdr(s, sp, SMB_COM_FLUSH);
753         pl16(p, fh);                    /* fid */
754         pbytes(p);
755         rc = cifsrpc(p);
756
757         free(p);
758         return rc;
759 }
760
761 /*
762  * Setting the time of last write to -1 gives "now" if the file
763  * was written and leaves it the same if the file wasn't written.
764  */
765 int
766 CIFSclose(Session *s, Share *sp, int fh)
767 {
768         int rc;
769         Pkt *p;
770
771         p = cifshdr(s, sp, SMB_COM_CLOSE);
772         pl16(p, fh);                    /* fid */
773         pl32(p, ~0L);                   /* Time of last write (none) */
774         pbytes(p);
775         rc = cifsrpc(p);
776
777         free(p);
778         return rc;
779 }
780
781
782 int
783 CIFSfindclose2(Session *s, Share *sp, int sh)
784 {
785         int rc;
786         Pkt *p;
787
788         p = cifshdr(s, sp, SMB_COM_FIND_CLOSE2);
789         pl16(p, sh);                    /* sid */
790         pbytes(p);
791         rc = cifsrpc(p);
792
793         free(p);
794         return rc;
795 }
796
797
798 int
799 CIFSecho(Session *s)
800 {
801         Pkt *p;
802         int rc;
803
804         p = cifshdr(s, nil, SMB_COM_ECHO);
805         pl16(p, 1);                             /* number of replies */
806         pbytes(p);
807         pascii(p, "abcdefghijklmnopqrstuvwxyz"); /* data */
808
809         rc = cifsrpc(p);
810         free(p);
811         return rc;
812 }
813
814
815 int
816 CIFSsetinfo(Session *s, Share *sp, char *path, FInfo *fip)
817 {
818         int rc;
819         Pkt *p;
820
821         p = cifshdr(s, sp, SMB_COM_SET_INFORMATION);
822         pl16(p, fip->attribs);
823         pl32(p, time(nil) - s->tz);     /* modified time */
824         pl64(p, 0);                     /* reserved */
825         pl16(p, 0);                     /* reserved */
826
827         pbytes(p);
828         p8(p, STR_ASCII);               /* buffer format */
829         ppath(p, path);
830
831         rc = cifsrpc(p);
832         free(p);
833         return rc;
834 }