]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/cifsd/smb.c
45a9aff5564ac76b14855320f41b85d6a2f9d6a1
[plan9front.git] / sys / src / cmd / ip / cifsd / smb.c
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include "dat.h"
5 #include "fns.h"
6
7 enum {
8         NEGOTIATE_USER_SECURITY = 1,
9         NEGOTIATE_ENCRYPT_PASSWORDS = 2,
10 };
11
12 static Chalstate *smbcs;
13 static int sessionkey;
14 static int sessionuid;
15 static int negotiated;
16
17 void
18 smbnegotiate(Req *r, uchar *h, uchar *p, uchar *e)
19 {
20         uchar *d, *de, *c, *ce, dom[256];
21         int i, x, mode;
22         char *s;
23
24         if(!unpack(h, p, e, "#0b{*2}#1w[]", &d, &de)){
25 err:
26                 r->respond(r, STATUS_INVALID_SMB);
27                 return;
28         }
29         i = 0;
30         x = -1;
31         while(unpack(h, d, de, "_f.", smbstrunpack8, &s, &d)){
32                 if(debug)
33                         fprint(2, "[%d] %s\n", i, s);
34                 if(x < 0 && !cistrcmp(s, "NT LM 0.12"))
35                         x = i;
36                 free(s);
37                 i++;
38         }
39         if(x < 0)
40                 x = i-1;
41         if(x < 0)
42                 x = 0;
43         sessionkey = rand();
44         c = ce = nil;
45         mode = 0;
46         if(needauth){
47                 if(smbcs != nil)
48                         auth_freechal(smbcs);
49                 if(smbcs = auth_challenge("proto=ntlm role=server")){
50                         c = (uchar*)smbcs->chal;
51                         ce = c + smbcs->nchal;
52                         mode = NEGOTIATE_USER_SECURITY | NEGOTIATE_ENCRYPT_PASSWORDS;
53                 } else
54                         logit("auth_challenge: %r");
55         }
56
57         /*
58          * <89> Section 2.2.4.52.2:  Windows NT servers always send the DomainName
59          *      field in Unicode characters and never add a padding byte for alignment.
60          *      Windows clients ignore the DomainName field in the server response.
61          */
62         d = dom;
63         de = dom + smbstrpack16(d, d, d + sizeof(dom), domain);
64         if(!pack(r->rh, r->rp, r->re, "#0b{*2wbwwllllvw#2b}#1w{[][]}.",
65                 x, mode, 50, 1, BUFFERSIZE, 0x10000, sessionkey,
66                 CAP_UNICODE | CAP_LARGEFILES | 
67                 CAP_NT_FIND | CAP_NT_SMBS | CAP_NT_STATUS,
68                 tofiletime(time(0)), -tzoff/60, c, ce, d, de, &r->rp))
69                 goto err;
70         negotiated = 1;
71         r->respond(r, 0);
72 }
73
74 enum {
75         SMB_SETUP_GUEST = 1,
76         SMB_SETUP_USE_LANMAN_KEY = 2,
77 };
78
79 void
80 smbsessionsetupandx(Req *r, uchar *h, uchar *p, uchar *e)
81 {
82         uchar *lm, *lme, *nt, *nte, *xp;
83         char *user, *dom, *os, *lanman;
84         int xcmd, cap, bs, sk;
85         AuthInfo *ai;
86
87         user = dom = os = lanman = nil;
88         if(!unpack(h, p, e, "#0b{*2b_@4ww____l#2w#3w____l}#1w{[][]ffff}{?.}",
89                 &xcmd, &bs, &sk, &cap, &lm, &lme, &nt, &nte,
90                 r->o->strunpack, &user, r->o->strunpack, &dom,
91                 r->o->strunpack, &os, r->o->strunpack, &lanman, &xp)){
92                 r->respond(r, STATUS_NOT_SUPPORTED);
93                 goto out;
94         }
95         if(debug)
96                 fprint(2, "bs=%x cap=%x user=%s dom=%s os=%s lanman=%s\n",
97                         bs, cap, user, dom, os, lanman);
98         if(sk != sessionkey)
99                 logit("ignoring bad session key");
100         while(!remoteuser){
101                 if(needauth){
102                         MSchapreply *mcr;
103
104                         if(smbcs == nil || strlen(user) == 0)
105                                 break;
106                         smbcs->user = user;
107                         smbcs->dom = dom;
108                         smbcs->nresp = (nte - nt)+sizeof(*mcr)-sizeof(mcr->NTresp);
109                         if(smbcs->nresp < sizeof(*mcr))
110                                 smbcs->nresp = sizeof(*mcr);
111                         mcr = mallocz(smbcs->nresp, 1);
112                         if((lme - lm) <= sizeof(mcr->LMresp))
113                                 memmove(mcr->LMresp, lm, lme - lm);
114                         if((nte - nt) > 0)
115                                 memmove(mcr->NTresp, nt, nte - nt);
116                         smbcs->resp = mcr;
117                         ai = auth_response(smbcs);
118                         if(ai == nil){
119                                 logit("auth_response: %r");
120                                 free(mcr);
121                                 break;  /* allow retry with the same challenge */
122                         }
123                         if(auth_chuid(ai, nil) < 0)
124                                 logit("auth_chuid: %r");
125                         auth_freeAI(ai);
126                         auth_freechal(smbcs);
127                         smbcs = nil;
128                         free(mcr);
129                 }
130                 remoteuser = getuser();
131                 logit("auth successfull");
132                 break;
133         }
134         sessionuid = (namehash(getuser()) & 0x7FFF) | 1;
135         r->uid = sessionuid;
136         if(bs >= 1024 || bs <= BUFFERSIZE)
137                 remotebuffersize = bs;
138         if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2ww}#1w{fff}{.}",
139                 xcmd, remoteuser ? 0 : SMB_SETUP_GUEST,
140                 r->o->strpack, osname, r->o->strpack, progname,
141                 r->o->strpack, domain, &r->rp))
142                 r->respond(r, STATUS_INVALID_SMB);
143         else
144                 smbcmd(r, xcmd, h, xp, e);
145 out:
146         free(user);
147         free(dom);
148         free(lanman);
149         free(os);
150 }
151
152 void
153 smblogoffandx(Req *r, uchar *h, uchar *p, uchar *e)
154 {
155         int xcmd;
156         uchar *xp;
157
158         if(!unpack(h, p, e, "#0b{*2b_}#1w{}{?.}", &xcmd, &xp)){
159 unsup:
160                 r->respond(r, STATUS_NOT_SUPPORTED);
161                 return;
162         }
163         logit("logoff");
164         if(remoteuser && needauth)
165                 goto unsup;
166         logoff();
167         remoteuser = nil;
168         r->tid = 0xFFFF;
169         r->uid = 0;
170         if(!pack(r->rh, r->rp, r->re, "#0b{*2b_}#1w{}{.}", xcmd, &r->rp))
171                 r->respond(r, STATUS_INVALID_SMB);
172         else
173                 smbcmd(r, xcmd, h, xp, e);
174 }
175
176 enum {
177         SMB_SUPPORT_SEARCH_BITS = 1,
178 };
179
180 void
181 smbtreeconnectandx(Req *r, uchar *h, uchar *p, uchar *e)
182 {
183         int err, xcmd, flags;
184         char *path, *service;
185         uchar *pw, *pwe, *xp;
186         Tree *t;
187
188         path = service = nil;
189         if(!unpack(h, p, e, "#0b{*2b_@3ww#2w}#1w{[]ff}{?.}",
190                 &xcmd, &flags, &pw, &pwe, r->o->strunpack, &path,
191                 smbstrunpack8, &service, &xp)){
192                 r->respond(r, STATUS_NOT_SUPPORTED);
193                 goto out;
194         }
195         if(r->flags & 1){
196                 disconnecttree(r->tid);
197                 r->tid = 0xFFFF;
198         }
199         if((t = connecttree(service, path, &err)) == nil){
200                 r->respond(r, err);
201                 goto out;
202         }
203         if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2ww}#1w{ff}{.}",
204                 xcmd, SMB_SUPPORT_SEARCH_BITS, 
205                 smbstrpack8, t->share->service,
206                 r->o->strpack, t->share->fsname, &r->rp)){
207                 disconnecttree(t->tid);
208                 r->respond(r, STATUS_INVALID_SMB);
209         } else {
210                 r->tid = t->tid;
211                 smbcmd(r, xcmd, h, xp, e);
212         }
213 out:
214         free(service);
215         free(path);
216 }
217
218 enum {
219         READ_WRITE_LOCK = 0x00,
220         SHARED_LOCK = 0x01,
221         OPLOCK_RELEASE = 0x02,
222         CHANGE_LOCKTYPE = 0x04,
223         CANCEL_LOCK = 0x08,
224         LARGE_FILES = 0x10,
225 };
226
227 void
228 smblockingandx(Req *r, uchar *h, uchar *p, uchar *e)
229 {
230         int i, err, xcmd, fid, tol, timeout, nunlock, nlock, pid;
231         unsigned int loff, hoff, llen, hlen;
232         uchar *d, *de, *xp;
233         vlong off, len;
234         File *f;
235
236         f = nil;
237         if(!unpack(h, p, e, "#0b{*2b_@2wwb_lww}#1w[]{?.}",
238                 &xcmd, &fid, &tol, &timeout, &nunlock, &nlock, &d, &de, &xp)){
239 unsup:
240                 r->respond(r, STATUS_NOT_SUPPORTED);
241                 goto out;
242         }
243         if((f = getfile(r->tid, fid, nil, &err)) == nil){
244                 r->respond(r, err);
245                 goto out;
246         }
247         if(debug)
248                 fprint(2, "tol %x\ntimeout %d\nunnlock %d\nnlock %d\n", tol, timeout, nunlock, nlock);
249         if(tol & (SHARED_LOCK | CHANGE_LOCKTYPE))
250                 goto unsup;
251         for(i=0; i<nunlock+nlock; i++){
252                 if(tol & LARGE_FILES){
253                         if(!unpack(d, d, de, "w__llll[]", &pid, &hoff, &loff, &hlen, &llen, &d, nil))
254                                 goto unsup;
255                 } else {
256                         if(!unpack(d, d, de, "wll[]", &pid, &loff, &llen, &d, nil))
257                                 goto unsup;
258                         hoff = hlen = 0;
259                 }
260                 off = (vlong)hoff<<32 | loff;
261                 len = (vlong)hlen<<32 | llen;
262                 if(debug)
263                         fprint(2, "%s %x %llux %llux\n", (i < nunlock) ? "unlock" : "lock", pid, off, len);
264         }
265         if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2w}#1w{}{.}", xcmd, &r->rp))
266                 r->respond(r, STATUS_INVALID_SMB);
267         else
268                 smbcmd(r, xcmd, h, xp, e);
269 out:
270         putfile(f);
271 }
272
273 enum {
274         REQ_ATTRIB = 0x01,
275         REQ_OPLOCK = 0x02,
276         REQ_OPLOCK_BATCH = 0x04,
277 };
278
279 void
280 smbopenandx(Req *r, uchar *h, uchar *p, uchar *e)
281 {
282         int err, nfid, xcmd, flags, amode, omode, fattr, act, csize, ctime;
283         char *name, *path;
284         uchar *xp;
285         Tree *t;
286         File *f;
287         Dir *d;
288
289         static int amode2dacc[] = {
290                 [0x00] GENERIC_READ,
291                 [0x01] GENERIC_WRITE,
292                 [0x02] GENERIC_READ | GENERIC_WRITE,
293                 [0x03] GENERIC_EXECUTE,
294         }, amode2sacc[] = {
295                 [0x00] FILE_SHARE_COMPAT, /* compat */
296                 [0x01] FILE_SHARE_NONE, /* exclusive use */
297                 [0x02] FILE_SHARE_READ, /* deny write */
298                 [0x03] FILE_SHARE_WRITE, /* deny read */
299                 [0x04] FILE_SHARE_READ | FILE_SHARE_WRITE, /* shared read/write */
300                 [0x05] -1,
301                 [0x06] -1,
302                 [0x07] -1,
303         }, omode2cdisp[] = {
304                 [0x00] -1,
305                 [0x01] FILE_OPEN,
306                 [0x02] FILE_OVERWRITE,
307                 [0x03] -1,
308                 [0x10] FILE_CREATE,
309                 [0x11] FILE_OPEN_IF,
310                 [0x12] FILE_OVERWRITE_IF,
311                 [0x13] -1,
312         };
313
314         f = nil;
315         d = nil;
316         name = path = nil;
317         if(!unpack(h, p, e, "#0b{*2b_@2www__wlwl________}#1w{f}{?.}",
318                 &xcmd, &flags, &amode, &fattr, &ctime, &omode,
319                 &csize, r->o->nameunpack, &name, &xp)){
320                 r->respond(r, STATUS_NOT_SUPPORTED);
321                 goto out;
322         }
323         if((path = getpath(r->tid, name, &t, &err)) == nil)
324                 goto errout;
325         if((f = createfile(path, r->namecmp,
326                 amode2dacc[amode & 3],  amode2sacc[(amode>>4) & 7],  omode2cdisp[omode & 0x13],
327                 FILE_NON_DIRECTORY_FILE, (ulong)csize, fattr, 
328                 &act, (flags & REQ_ATTRIB) ? &d : nil, &err)) == nil){
329 errout:
330                 r->respond(r, err);
331                 goto out;
332         }
333         nfid = newfid(t, f);
334         amode = -1;
335         if(f->dacc & READMASK)
336                 amode += 1;
337         if(f->dacc & WRITEMASK)
338                 amode += 2;
339         if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2wwwllww__w______}#1w{}{.}",
340                 xcmd, nfid,
341                 !d ? 0 : dosfileattr(d),
342                 !d ? 0 : d->mtime+tzoff,
343                 !d ? 0 : filesize32(d->length),
344                 !d ? 0 : amode, 
345                 !d ? 0 : f->rtype,
346                 !d ? 0 : act, &r->rp)){
347                 delfid(t, nfid);
348                 r->respond(r, STATUS_INVALID_SMB);
349         } else
350                 smbcmd(r, xcmd, h, xp, e);
351 out:
352         free(name);
353         free(path);
354         putfile(f);
355         free(d);
356 }
357
358 enum {
359         NT_CREATE_REQUEST_OPLOCK = 0x02,
360         NT_CREATE_REQUEST_OPBATCH = 0x04,
361         NT_CREATE_OPEN_TARGET_DIR = 0x08,
362 };
363
364 void
365 smbntcreatendx(Req *r, uchar *h, uchar *p, uchar *e)
366 {
367         int err, nfid, xcmd, flags, rootfid, fattr, dacc, sacc, cdisp, copt, act;
368         char *name, *path;
369         vlong csize;
370         uchar *xp;
371         Tree *t;
372         File *f;
373         Dir *d;
374
375         f = nil;
376         d = nil;
377         name = path = nil;
378         if(!unpack(h, p, e, "#0b{*2b_@2w___lllvllll_____}#1w{f}{?.}",
379                 &xcmd, &flags, &rootfid, &dacc, &csize, &fattr, &sacc, &cdisp, &copt,
380                 r->o->nameunpack, &name, &xp)){
381                 r->respond(r, STATUS_NOT_SUPPORTED);
382                 goto out;
383         }
384         if(rootfid){
385                 if((f = getfile(r->tid, rootfid, &t, &err)) == nil)
386                         goto errout;
387                 path = conspath(f->path, name);
388                 putfile(f);
389         } else if((path = getpath(r->tid, name, &t, &err)) == nil)
390                 goto errout;
391         if((f = createfile(path, r->namecmp, dacc, sacc, cdisp, copt, csize, fattr, &act, &d, &err)) == nil){
392 errout:
393                 r->respond(r, err);
394                 goto out;
395         }
396         nfid = newfid(t, f);
397         if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2wbwlvvvvlvvw__b}#1w{}{.}",
398                 xcmd, 0, nfid, act, tofiletime(d->mtime), tofiletime(d->atime),
399                 tofiletime(d->mtime), tofiletime(d->mtime), extfileattr(d),
400                 allocsize(d->length, t->share->blocksize), 
401                 d->length, f->rtype, (d->qid.type & QTDIR) != 0, &r->rp)){
402                 delfid(t, nfid);
403                 r->respond(r, STATUS_INVALID_SMB);
404         } else
405                 smbcmd(r, xcmd, h, xp, e);
406 out:
407         free(name);
408         free(path);
409         putfile(f);
410         free(d);
411 }
412
413 void
414 smbreadandx(Req *r, uchar *h, uchar *p, uchar *e)
415 {
416         int n, xcmd, fid, mincount, maxcount;
417         unsigned int loff, hoff;
418         uchar *rb, *rp, *re, *xp;
419         vlong off;
420         File *f;
421
422         f = nil;
423         hoff = 0;
424         if((unpack(h, p, e, "#0b{*2b_@2wwlww______l}#1w{}{?.}",
425                 &xcmd, &fid, &loff, &mincount, &maxcount, &hoff, &xp) == 0) &&
426            (unpack(h, p, e, "#0b{*2b_@2wwlww______}#1w{}{?.}",
427                 &xcmd, &fid, &loff, &mincount, &maxcount, &xp) == 0)){
428                 r->respond(r, STATUS_NOT_SUPPORTED);
429                 goto out;
430         }
431         if((f = getfile(r->tid, fid, nil, &n)) == nil){
432                 r->respond(r, n);
433                 goto out;
434         }
435         if((f->fd < 0) || (f->dacc & READMASK) == 0){
436                 r->respond(r, STATUS_ACCESS_DENIED);
437                 goto out;
438         }
439         /* dont really pack, just to get the pointer to the response data */
440         if(!pack(r->rh, r->rp, r->re, "#0b{*2________________________}#1w{%2.}", &rb)){
441 badsmb:
442                 r->respond(r, STATUS_INVALID_SMB);
443                 goto out;
444         }
445         re = rb + mincount;
446         if(re > r->re)
447                 goto badsmb;
448         if(maxcount > mincount){
449                 re = rb + maxcount;
450                 if(re > r->re)
451                         re = r->re;
452         }
453         n = 0;
454         rp = rb;
455         off = (vlong)hoff<<32 | loff;
456         while(rp < re){
457                 if((n = pread(f->fd, rp, re - rp, off)) <= 0)
458                         break;
459                 off += n;
460                 rp += n;
461         }
462         if(n < 0){
463                 r->respond(r, smbmkerror());
464                 goto out;
465         }
466         if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@3www__#2w@2w__________}#1w{%2[]}{.}",
467                 xcmd, 0xFFFF, 0x0000, rb, rp, &r->rp))
468                 goto badsmb;
469         smbcmd(r, xcmd, h, xp, e);
470 out:
471         putfile(f);
472 }
473
474 void
475 smbwriteandx(Req *r, uchar *h, uchar *p, uchar *e)
476 {
477         int n, xcmd, fid, bufoff, buflen;
478         unsigned int loff, hoff;
479         uchar *d, *de, *xp;
480         File *f;
481
482         f = nil;
483         hoff = 0;
484         if((unpack(h, p, e, "#0b{*2b_@2wwl__________wwl}#1w{}{?.}",
485                 &xcmd, &fid, &loff, &buflen, &bufoff, &hoff, &xp) == 0) &&
486            (unpack(h, p, e, "#0b{*2b_@2wwl__________ww}#1w{}{?.}",
487                 &xcmd, &fid, &loff, &buflen, &bufoff, &xp) == 0)){
488                 r->respond(r, STATUS_NOT_SUPPORTED);
489                 goto out;
490         }
491         d = h + bufoff;
492         de = d + buflen;
493         if(d < h || de > e){
494 badsmb:
495                 r->respond(r, STATUS_INVALID_SMB);
496                 goto out;
497         }
498         if((f = getfile(r->tid, fid, nil, &n)) == nil){
499                 r->respond(r, n);
500                 goto out;
501         }
502         if((f->fd < 0) || (f->dacc & WRITEMASK) == 0){
503                 r->respond(r, STATUS_ACCESS_DENIED);
504                 goto out;
505         }
506         if((n = pwrite(f->fd, d, de - d, (vlong)hoff<<32 | loff)) < 0){
507                 r->respond(r, smbmkerror());
508                 goto out;
509         }
510         if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2www____}#1w{}{.}", xcmd, n, 0xFFFF, &r->rp))
511                 goto badsmb;
512         smbcmd(r, xcmd, h, xp, e);
513 out:
514         putfile(f);
515 }
516
517 void
518 smbwrite(Req *r, uchar *h, uchar *p, uchar *e)
519 {
520         int n, fid, count, bf;
521         unsigned int off;
522         uchar *d, *de;
523         File *f;
524
525         f = nil;
526         if(!unpack(h, p, e, "#0b{*2wwl__}#1w{b#2w[]}", &fid, &count, &off, &bf, &d, &de)){
527 unsup:
528                 r->respond(r, STATUS_NOT_SUPPORTED);
529                 goto out;
530         }
531         if(bf != 0x1)
532                 goto unsup;
533         if((f = getfile(r->tid, fid, nil, &n)) == nil){
534                 r->respond(r, n);
535                 goto out;
536         }
537         if((f->fd < 0) || (f->dacc & WRITEMASK) == 0){
538                 r->respond(r, STATUS_ACCESS_DENIED);
539                 goto out;
540         }
541         if(count != (de - d)){
542                 r->respond(r, STATUS_INVALID_SMB);
543                 goto out;
544         }
545         if((n = pwrite(f->fd, d, count, off)) < 0){
546                 r->respond(r, smbmkerror());
547                 goto out;
548         }
549         if(!pack(r->rh, r->rp, r->re, "#0b{*2w}#1w{}.", n, &r->rp))
550                 r->respond(r, STATUS_INVALID_SMB);
551         else
552                 r->respond(r, 0);
553 out:
554         putfile(f);
555 }
556
557 void
558 smbcloseflush(Req *r, uchar *h, uchar *p, uchar *e)
559 {
560         int err, fid;
561         Tree *t;
562         Find *s;
563         File *f;
564
565         f = nil;
566         s = nil;
567         if(!unpack(h, p, e, "#0b{*2w}#1w{}", &fid)){
568                 r->respond(r, STATUS_NOT_SUPPORTED);
569                 goto out;
570         }
571         switch(r->cmd){
572         case 0x05: /* SMB_COM_FLUSH */
573                 if(fid == 0xFFFF){
574                         if(gettree(r->tid) == nil){
575                                 r->respond(r, STATUS_SMB_BAD_TID);
576                                 goto out;
577                         }
578                         break;
579                 }
580                 /* no break */
581         case 0x04: /* SMB_COM_CLOSE */
582                 if((f = getfile(r->tid, fid, &t, &err)) == nil){
583                         r->respond(r, err);
584                         goto out;
585                 }
586                 if(r->cmd == 0x04) 
587                         delfid(t, fid);
588                 break;
589         case 0x34: /* SMB_COM_FIND_CLOSE2 */
590                 if((s = getfind(r->tid, fid, &t, &err)) == nil){
591                         r->respond(r, err);
592                         goto out;
593                 }
594                 delsid(t, fid);
595                 break;
596         }
597         if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
598                 r->respond(r, STATUS_INVALID_SMB);
599         else
600                 r->respond(r, 0);
601 out:
602         putfile(f);
603         putfind(s);
604 }
605
606 void
607 smbcreatedirectory(Req *r, uchar *h, uchar *p, uchar *e)
608 {
609         char *name, *path;
610         int err, fd;
611
612         name = path = nil;
613         if(!unpack(h, p, e, "#0b{*2}#1w{_f}", r->o->nameunpack, &name)){
614                 r->respond(r, STATUS_NOT_SUPPORTED);
615                 goto out;
616         }
617         if((path = getpath(r->tid, name, nil, &err)) == nil){
618                 r->respond(r, err);
619                 goto out;
620         }
621         if(access(path, AEXIST) == 0){
622                 r->respond(r, STATUS_OBJECT_NAME_COLLISION);
623                 goto out;
624         }
625         if((fd = create(path, OREAD, DMDIR | 0777)) < 0){
626                 r->respond(r, smbmkerror());
627                 goto out;
628         }
629         close(fd);
630         if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
631                 r->respond(r, STATUS_INVALID_SMB);
632         else
633                 r->respond(r, 0);
634 out:
635         free(name);
636         free(path);
637 }
638
639 void
640 smbrename(Req *r, uchar *h, uchar *p, uchar *e)
641 {
642         char *name1, *name2, *path1, *path2, *x, *y;
643         int err, sattr;
644         Dir *d, nd;
645
646         d = nil;
647         name1 = name2 = path1 = path2 = nil;
648         if(!unpack(h, p, e, "#0b{*2w}#1w{_f_f}", &sattr,
649                 r->o->nameunpack, &name1, r->o->nameunpack, &name2)){
650                 r->respond(r, STATUS_NOT_SUPPORTED);
651                 goto out;
652         }
653         if((path1 = getpath(r->tid, name1, nil, &err)) == nil){
654                 r->respond(r, err);
655                 goto out;
656         }
657         if((path2 = getpath(r->tid, name2, nil, &err)) == nil){
658                 r->respond(r, err);
659                 goto out;
660         }
661         if((d = xdirstat(&path1, r->namecmp)) == nil){
662                 r->respond(r, smbmkerror());
663                 goto out;
664         }
665         if(!matchattr(d, sattr)){
666                 r->respond(r, STATUS_NO_SUCH_FILE);
667                 goto out;
668         }
669
670         if(x = strrchr(path1, '/')){
671                 *x = 0;
672         } else {
673 badpath:
674                 r->respond(r, STATUS_OBJECT_PATH_SYNTAX_BAD);
675                 goto out;
676         }
677         if(y = strrchr(path2, '/'))
678                 *y++ = 0;
679         else
680                 goto badpath;
681         if(r->namecmp(path1, path2)){
682                 r->respond(r, STATUS_NOT_SAME_DEVICE);
683                 goto out;
684         }
685         *x = '/';
686         nulldir(&nd);
687         nd.name = y;
688         if(dirwstat(path1, &nd) < 0){
689                 r->respond(r, smbmkerror());
690                 goto out;
691         }
692         if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
693                 r->respond(r, STATUS_INVALID_SMB);
694         else
695                 r->respond(r, 0);
696         xdirflush(path1, r->namecmp);
697 out:
698         free(name1);
699         free(name2);
700         free(path1);
701         free(path2);
702         free(d);
703 }
704
705 void
706 smbdelete(Req *r, uchar *h, uchar *p, uchar *e)
707 {
708         char *name, *path, *tmp;
709         int n, err, i, sattr;
710         Find *f;
711         Dir *d;
712
713         f = nil;
714         name = path = nil;
715         if(!unpack(h, p, e, "#0b{*2w}#1w{_f}", &sattr, r->o->nameunpack, &name)){
716                 r->respond(r, STATUS_NOT_SUPPORTED);
717                 goto out;
718         }
719         if((path = getpath(r->tid, name, nil, &err)) == nil){
720 errout:
721                 r->respond(r, err);
722                 goto out;
723         }
724         if((f = openfind(path, r->namecmp, sattr, 0, &err)) == nil)
725                 goto errout;
726         n = 0;
727         while((i = readfind(f, f->index, &d)) >= 0){
728                 tmp = conspath(f->base, d->name);
729                 if(remove(tmp) < 0){
730                         err = smbmkerror();
731                         free(tmp);
732                         goto errout;
733                 }
734                 free(tmp);
735                 f->index = i+1;
736                 n++;
737         }
738         if(n == 0){
739                 err = STATUS_NO_SUCH_FILE;
740                 goto errout;
741         }
742         if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
743                 r->respond(r, STATUS_INVALID_SMB);
744         else
745                 r->respond(r, 0);
746 out:
747         free(name);
748         free(path);
749         putfind(f);
750 }
751
752 void
753 smbdeletedirectory(Req *r, uchar *h, uchar *p, uchar *e)
754 {
755         char *name, *path;
756         Dir *d;
757         int err;
758
759         name = path = nil;
760         if(!unpack(h, p, e, "#0b{*2}#1w{_f}", r->o->nameunpack, &name)){
761                 r->respond(r, STATUS_NOT_SUPPORTED);
762                 goto out;
763         }
764         if((path = getpath(r->tid, name, nil, &err)) == nil){
765                 r->respond(r, err);
766                 goto out;
767         }
768         if(remove(path) < 0){
769                 err = smbmkerror();
770                 if((d = xdirstat(&path, r->namecmp)) == nil){
771                         r->respond(r, err);
772                         goto out;
773                 }
774                 free(d);
775                 if(remove(path) < 0){
776                         r->respond(r, smbmkerror());
777                         goto out;
778                 }
779         }
780         if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
781                 r->respond(r, STATUS_INVALID_SMB);
782         else
783                 r->respond(r, 0);
784 out:
785         free(name);
786         free(path);
787 }
788
789 void
790 smbecho(Req *r, uchar *h, uchar *p, uchar *e)
791 {
792         uchar *t, *d, *de;
793         int i, n;
794
795         if(!unpack(h, p, e, "#0b{*2w}#1w[]", &n, &d, &de)){
796                 r->respond(r, STATUS_NOT_SUPPORTED);
797                 return;
798         }
799         if((r->tid != 0xFFFF) && (gettree(r->tid) == nil)){
800                 r->respond(r, STATUS_SMB_BAD_TID);
801                 return;
802         }
803         t = r->rp;
804         for(i=0; i < n; i++){
805                 if(!pack(r->rh, r->rp, r->re, "#0b{*2w}#1w[].", i, d, de, &r->rp)){
806                         r->respond(r, STATUS_INVALID_SMB);
807                         break;
808                 }
809                 r->respond(r, 0);
810                 r->rp = t;
811         }
812 }
813
814 void
815 smbdisconnecttree(Req *r, uchar *h, uchar *p, uchar *e)
816 {
817         int err;
818
819         if(!unpack(h, p, e, "#0b{*2}#1w{}")){
820                 r->respond(r, STATUS_NOT_SUPPORTED);
821                 return;
822         }
823         if(err = disconnecttree(r->tid)){
824                 r->respond(r, err);
825                 return;
826         }
827         if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
828                 r->respond(r, STATUS_INVALID_SMB);
829         else
830                 r->respond(r, 0);
831 }
832
833 void
834 smbqueryinformation(Req *r, uchar *h, uchar *p, uchar *e)
835 {
836         char *name, *path;
837         int err, mtime;
838         Dir *d;
839
840         d = nil;
841         name = path = nil;
842         if(!unpack(h, p, e, "#0b{*2}#1w{_f}", r->o->nameunpack, &name)){
843                 r->respond(r, STATUS_NOT_SUPPORTED);
844                 goto out;
845         }
846         if((path = getpath(r->tid, name, nil, &err)) == nil){
847                 r->respond(r, err);
848                 goto out;
849         }
850         if((d = xdirstat(&path, r->namecmp)) == nil){
851                 r->respond(r, smbmkerror());
852                 goto out;
853         }
854         mtime = d->mtime + tzoff;
855         if(!pack(r->rh, r->rp, r->re, "#0b{*2wll__________}#1w{}.",
856                 dosfileattr(d), mtime, filesize32(d->length), &r->rp))
857                 r->respond(r, STATUS_INVALID_SMB);
858         else
859                 r->respond(r, 0);
860 out:
861         free(name);
862         free(path);
863         free(d);
864 }
865
866 void
867 smbsetinformation(Req *r, uchar *h, uchar *p, uchar *e)
868 {
869         char *name, *path;
870         int err, attr, mtime;
871         Dir *d, nd;
872
873         d = nil;
874         name = path = nil;
875         if(!unpack(h, p, e, "#0b{*2wl__________}#1w{_f}", &attr, &mtime, r->o->nameunpack, &name)){
876                 r->respond(r, STATUS_NOT_SUPPORTED);
877                 goto out;
878         }
879         if((path = getpath(r->tid, name, nil, &err)) == nil){
880                 r->respond(r, err);
881                 goto out;
882         }
883         if((d = xdirstat(&path, r->namecmp)) == nil){
884                 r->respond(r, smbmkerror());
885                 goto out;
886         }
887         nulldir(&nd);
888         if(mtime)
889                 nd.mtime = mtime-tzoff;
890         nd.mode = d->mode;
891         if(attr & ATTR_READONLY){
892                 if(nd.mode & 0222)
893                         nd.mode &= ~0222;
894         }else{
895                 if((nd.mode & 0222) == 0)
896                         nd.mode |= 0222;
897         }
898         if(attr & ATTR_ARCHIVE)
899                 nd.mode &= ~DMTMP;
900         else
901                 nd.mode |= DMTMP;
902         if(nd.mode == d->mode)
903                 nd.mode = ~0;
904         if(dirwstat(path, &nd) < 0){
905                 r->respond(r, smbmkerror());
906                 goto out;
907         }
908         if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
909                 r->respond(r, STATUS_INVALID_SMB);
910         else
911                 r->respond(r, 0);
912         xdirflush(path, r->namecmp);
913 out:
914         free(name);
915         free(path);
916         free(d);
917 }
918
919 void
920 smbcheckdirectory(Req *r, uchar *h, uchar *p, uchar *e)
921 {
922         char *name, *path;
923         int err;
924         Dir *d;
925
926         d = nil;
927         name = path = nil;
928         if(!unpack(h, p, e, "#0b{*2}#1w{_f}", r->o->nameunpack, &name)){
929                 r->respond(r, STATUS_NOT_SUPPORTED);
930                 goto out;
931         }
932         if((path = getpath(r->tid, name, nil, &err)) == nil){
933                 r->respond(r, err);
934                 goto out;
935         }
936         if((d = xdirstat(&path, r->namecmp)) == nil){
937                 r->respond(r, smbmkerror());
938                 goto out;
939         }
940         if((d->qid.type & QTDIR) == 0){
941                 r->respond(r, STATUS_OBJECT_PATH_NOT_FOUND);
942                 goto out;
943         }
944         if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp))
945                 r->respond(r, STATUS_INVALID_SMB);
946         else
947                 r->respond(r, 0);
948 out:
949         free(name);
950         free(path);
951         free(d);
952 }
953
954 void
955 smbqueryinformation2(Req *r, uchar *h, uchar *p, uchar *e)
956 {
957         int err, fid, adate, atime, mdate, mtime;
958         Tree *t;
959         File *f;
960         Dir *d;
961
962         f = nil;
963         t = nil;
964         d = nil;
965         if(!unpack(h, p, e, "#0b{*2w}#1w{}", &fid)){
966                 r->respond(r, STATUS_NOT_SUPPORTED);
967                 goto out;
968         }
969         if((f = getfile(r->tid, fid, &t, &err)) == nil){
970                 r->respond(r, err);
971                 goto out;
972         }
973         if((d = statfile(f)) == nil){
974                 r->respond(r, smbmkerror());
975                 goto out;
976         }
977         todatetime(d->atime+tzoff, &adate, &atime);
978         todatetime(d->mtime+tzoff, &mdate, &mtime);
979         if(!pack(r->rh, r->rp, r->re, "#0b{*2wwwwwwllw}#1w{}.",
980                 mdate, mtime, adate, atime, mdate, mtime,
981                 filesize32(d->length), filesize32(allocsize(d->length, t->share->blocksize)),
982                 dosfileattr(d), &r->rp))
983                 r->respond(r, STATUS_INVALID_SMB);
984         else
985                 r->respond(r, 0);
986 out:
987         putfile(f);
988         free(d);
989 }
990
991 void
992 smbqueryinformationdisk(Req *r, uchar *h, uchar *p, uchar *e)
993 {
994         Tree *t;
995         Share *s;
996
997         if(!unpack(h, p, e, "#0b{*2}#1w{}")){
998                 r->respond(r, STATUS_NOT_SUPPORTED);
999                 return;
1000         }
1001         if((t = gettree(r->tid)) == nil){
1002                 r->respond(r, STATUS_SMB_BAD_TID);
1003                 return;
1004         }
1005         s = t->share;
1006         if(!pack(r->rh, r->rp, r->re, "#0b{*2wwww__}#1w{}.",
1007                 (int)(allocsize(s->allocsize + s->freesize, s->blocksize) / s->blocksize),
1008                 s->blocksize / s->sectorsize, s->sectorsize,
1009                 (int)(allocsize(s->freesize, s->blocksize) / s->blocksize), &r->rp))
1010                 r->respond(r, STATUS_INVALID_SMB);
1011         else
1012                 r->respond(r, 0);
1013 }
1014
1015 static int
1016 fpackdir(Req *r, Dir *d, Tree *t, int i, int level, uchar *b, uchar *p, uchar *e, uchar **prevoff, uchar **nameoff)
1017 {
1018         vlong atime, mtime, alen, dlen;
1019         uchar shortname[2*12];
1020         uchar *namep;
1021         Share *share;
1022         int n;
1023
1024         share = t->share;
1025         dlen = d->length;
1026         alen = allocsize(dlen, share->blocksize);
1027         atime = tofiletime(d->atime);
1028         mtime = tofiletime(d->mtime);
1029         memset(shortname, 0, sizeof(shortname));
1030
1031         switch(level){
1032         case 0x0101:    /* SMB_FIND_FILE_DIRECTORY_INFO */
1033                 n = pack(b, p, e, "llvvvvvvl#0l{.f}%4",
1034                         0, i, mtime, atime, mtime, mtime, dlen, alen, extfileattr(d),
1035                         &namep, r->o->untermnamepack, d->name);
1036                 break;
1037
1038         case 0x0102:    /* SMB_FIND_FILE_FULL_DIRECTORY_INFO */
1039                 n = pack(b, p, e, "llvvvvvvl#0ll{.f}%4",
1040                         0, i, mtime, atime, mtime, mtime, dlen, alen, extfileattr(d), 0,
1041                         &namep, r->o->untermnamepack, d->name);
1042                 break;
1043
1044         case 0x0103:    /* SMB_FIND_FILE_NAMES_INFO */
1045                 n = pack(b, p, e, "ll#0l{.f}%4",
1046                         0, i, &namep, r->o->untermnamepack, d->name);
1047                 break;
1048
1049         case 0x0104:    /* SMB_FIND_FILE_BOTH_DIRECTORY_INFO */
1050                 n = pack(b, p, e, "llvvvvvvl#1l#2lb_[]{.f}{}____%4",
1051                         0, i, mtime, atime, mtime, mtime, dlen, alen, extfileattr(d),
1052                         0, shortname, shortname+sizeof(shortname),
1053                         &namep, r->o->untermnamepack, d->name);
1054                 break;
1055
1056         default:
1057                 logit("[%.4x] unknown FIND infolevel", level);
1058                 return -1;
1059         }
1060         if(n <= 0)
1061                 return 0;
1062         if(nameoff)
1063                 *nameoff = namep;
1064         if(prevoff && *prevoff)
1065                 pack(b, *prevoff, e, "l", (int)(p - *prevoff));
1066         if(prevoff)
1067                 *prevoff = p;
1068         return n;
1069 }
1070
1071 static int
1072 qpackdir(Req *, Dir *d, Tree *t, File *f, int level, uchar *b, uchar *p, uchar *e)
1073 {
1074         vlong atime, mtime, dlen, alen;
1075         int link, delete, isdir;
1076         Share *share;
1077
1078         if(debug)
1079                 fprint(2, "QYERY level %.4x\n", level);
1080
1081         share = t->share;
1082         dlen = d->length;
1083         alen = allocsize(dlen, share->blocksize);
1084         atime = tofiletime(d->atime);
1085         mtime = tofiletime(d->mtime);
1086         isdir = (d->qid.type & QTDIR) != 0;
1087         delete = f && deletedfile(f);
1088         link = !delete;
1089
1090         switch(level){
1091         case 0x0101:    /* SMB_QUERY_FILE_BASIC_INFO */
1092                 return pack(b, p, e, "vvvvl____", mtime, atime, mtime, mtime, extfileattr(d));
1093
1094         case 0x0102:    /* SMB_QUERY_FILE_STANDARD_INFO */
1095                 return pack(b, p, e, "vvlbb", alen, dlen, link, delete, isdir);
1096
1097         case 0x0103:    /* SMB_QUERY_FILE_EA_INFO */
1098                 return pack(b, p, e, "l", 0);
1099
1100         case 0x0107:    /* SMB_QUERY_FILE_ALL_INFO */
1101                 return pack(b, p, e, "vvvvl____vvlbb__#1l#0l{f}{}", 
1102                         mtime, atime, mtime, mtime, extfileattr(d), alen, dlen, link,  delete, isdir,
1103                         smbuntermnamepack16, d->name);
1104
1105         case 0x0109:    /* SMB_QUERY_FILE_STREAM_INFO */
1106                 if(isdir)
1107                         return 0;
1108                 return pack(b, p, e, "l#0lvv{f}", 0, dlen, alen, smbuntermstrpack16, "::$DATA");
1109
1110         default:
1111                 logit("[%.4x] unknown QUERY infolevel", level);
1112                 return -1;
1113         }
1114 }
1115
1116 void
1117 trans2querypathinformation(Trans *t)
1118 {
1119         char *name, *path;
1120         Tree *tree;
1121         int n, level;
1122         Dir *d;
1123
1124         d = nil;
1125         path = name = nil;
1126         if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "w____f",
1127                 &level, t->o->nameunpack, &name)){
1128                 t->respond(t, STATUS_NOT_SUPPORTED);
1129                 goto out;
1130         }
1131         if((path = getpath(t->r->tid, name, &tree, &n)) == nil){
1132                 t->respond(t, n);
1133                 goto out;
1134         }
1135         if((d = xdirstat(&path, t->namecmp)) == nil){
1136                 t->respond(t, smbmkerror());
1137                 goto out;
1138         }
1139         pack(t->out.param.b, t->out.param.p, t->out.param.e, "__.", &t->out.param.p);
1140         if((n = qpackdir(t->r, d, tree, nil, level, t->out.data.b, t->out.data.p, t->out.data.e)) < 0)
1141                 t->respond(t, STATUS_OS2_INVALID_LEVEL);
1142         else {
1143                 t->out.data.p += n;
1144                 t->respond(t, 0);
1145         }
1146 out:
1147         free(name);
1148         free(path);
1149         free(d);
1150 }
1151
1152 void
1153 trans2queryfileinformation(Trans *t)
1154 {
1155         int n, fid, level;
1156         Tree *tree;
1157         File *f;
1158         Dir *d;
1159
1160         f = nil;
1161         d = nil;
1162         if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "ww", &fid, &level)){
1163                 t->respond(t, STATUS_NOT_SUPPORTED);
1164                 goto out;
1165         }
1166         if((f = getfile(t->r->tid, fid, &tree, &n)) == nil){
1167                 t->respond(t, n);
1168                 goto out;
1169         }
1170         if((d = statfile(f)) == nil){
1171                 t->respond(t, smbmkerror());
1172                 goto out;
1173         }
1174         pack(t->out.param.b, t->out.param.p, t->out.param.e, "__.", &t->out.param.p);
1175         if((n = qpackdir(t->r, d, tree, f, level, t->out.data.b, t->out.data.p, t->out.data.e)) < 0)
1176                 t->respond(t, STATUS_OS2_INVALID_LEVEL);
1177         else {
1178                 t->out.data.p += n;
1179                 t->respond(t, 0);
1180         }
1181 out:
1182         putfile(f);
1183         free(d);
1184 }
1185
1186 static int
1187 setfilepathinformation(Req *r, Dir *d, File *f, char *path, int level, uchar *b, uchar *p, uchar *e)
1188 {
1189         int attr, adt, atm, mdt, mtm, delete;
1190         vlong len, atime, mtime;
1191         Dir nd;
1192
1193         nulldir(&nd);
1194         if(debug)
1195                 fprint(2, "SET level %.4x\n", level);
1196         switch(level){
1197         case 0x0001:    /* SMB_INFO_STANDARD */
1198                 if(!unpack(b, p, e, "____wwww__________", &adt, &atm, &mdt, &mtm))
1199                         goto unsup;
1200                 nd.atime = fromdatetime(adt, atm)-tzoff;
1201                 nd.mtime = fromdatetime(mdt, mtm)-tzoff;
1202                 break;
1203
1204         case 0x0101:    /* SMB_SET_FILE_BASIC_INFO */
1205                 if(f == nil || !unpack(b, p, e, "________vv________l____", &atime, &mtime, &attr))
1206                         goto unsup;
1207                 if(atime && atime != -1LL)
1208                         nd.atime = fromfiletime(atime);
1209                 if(mtime && mtime != -1LL)
1210                         nd.mtime = fromfiletime(mtime);
1211                 if(attr){
1212                         if(attr & ATTR_READONLY){
1213                                 if(d->mode & 0222)
1214                                         nd.mode = d->mode & ~0222;
1215                         } else {
1216                                 if((d->mode & 0222) == 0)
1217                                         nd.mode = d->mode | 0222;
1218                         }
1219                 }
1220                 break;
1221
1222         case 0x0102:    /* SMB_SET_FILE_DISPOSITION_INFO */
1223                 if(f == nil || !unpack(b, p, e, "b", &delete))
1224                         goto unsup;
1225                 if((f->dacc & FILE_DELETE) == 0)
1226                         return STATUS_ACCESS_DENIED;
1227                 deletefile(f, delete);
1228                 break;
1229
1230         case 0x0103:    /* SMB_SET_FILE_ALLOCATION_INFO */
1231         case 0x0104:    /* SMB_SET_FILE_END_OF_FILE_INFO */
1232                 if(f == nil || !unpack(b, p, e, "v", &len))
1233                         goto unsup;
1234                 if(d->qid.type & QTDIR)
1235                         return STATUS_OS2_INVALID_ACCESS;
1236                 if(len != -1LL)
1237                         nd.length = len;
1238                 break;
1239
1240         default:
1241                 logit("[%.4x] unknown SET infolevel", level);
1242                 return STATUS_OS2_INVALID_LEVEL;
1243         unsup:
1244                 return STATUS_NOT_SUPPORTED;
1245         }
1246         if(debug)
1247                 fprint(2, "wstat\nmode %lo\natime %ld\nmtime %ld\nlength %llux\n",
1248                         nd.mode, nd.atime, nd.mtime, nd.length);
1249         if(((f && f->fd >= 0) ? dirfwstat(f->fd, &nd) : dirwstat(path, &nd)) < 0)
1250                 return smbmkerror();
1251         xdirflush(path, r->namecmp);
1252         return 0;
1253 }
1254
1255 void
1256 trans2setpathinformation(Trans *t)
1257 {
1258         int err, level;
1259         Tree *tree;
1260         char *name, *path;
1261         Dir *d;
1262
1263         d = nil;
1264         name = path = nil;
1265         if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "w____f", &level, 
1266                 t->o->nameunpack, &name)){
1267                 t->respond(t, STATUS_NOT_SUPPORTED);
1268                 goto out;
1269         }
1270         if((path = getpath(t->r->tid, name, &tree, &err)) == nil)
1271                 goto errout;
1272         if((d = xdirstat(&path, t->namecmp)) == nil){
1273                 t->respond(t, smbmkerror());
1274                 goto out;
1275         }
1276         if(err = setfilepathinformation(t->r, d, nil, path, level, t->in.data.b, t->in.data.p, t->in.data.e)){
1277 errout:
1278                 t->respond(t, err);
1279                 goto out;
1280         }
1281         pack(t->out.param.b, t->out.param.p, t->out.param.e, "__.", &t->out.param.p);
1282         t->respond(t, 0);
1283 out:
1284         free(name);
1285         free(path);
1286         free(d);
1287 }
1288
1289 void
1290 trans2setfileinformation(Trans *t)
1291 {
1292         int err, fid, level;
1293         Tree *tree;
1294         File *f;
1295         Dir *d;
1296
1297         f = nil;
1298         d = nil;
1299         if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "ww__", &fid, &level)){
1300                 t->respond(t, STATUS_NOT_SUPPORTED);
1301                 goto out;
1302         }
1303         if((f = getfile(t->r->tid, fid, &tree, &err)) == nil)
1304                 goto errout;
1305         if((d = statfile(f)) == nil){
1306                 t->respond(t, smbmkerror());
1307                 goto out;
1308         }
1309         if(err = setfilepathinformation(t->r, d, f, f->path, level, t->in.data.b, t->in.data.p, t->in.data.e)){
1310 errout:
1311                 t->respond(t, err);
1312                 goto out;
1313         }
1314         pack(t->out.param.b, t->out.param.p, t->out.param.e, "__.", &t->out.param.p);
1315         t->respond(t, 0);
1316 out:
1317         putfile(f);
1318         free(d);
1319 }
1320
1321 enum {
1322         FILE_CASE_SENSITIVE_SEARCH = 1,
1323         FILE_CASE_PRESERVED_NAMES = 2,
1324         FILE_UNICODE_ON_DISK = 4,
1325 };
1326
1327 void
1328 trans2queryfsinformation(Trans *t)
1329 {
1330         int n, level;
1331         Share *share;
1332         Tree *tree;
1333         char *s;
1334
1335         s = nil;
1336         if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "w", &level)){
1337                 t->respond(t, STATUS_NOT_SUPPORTED);
1338                 goto out;
1339         }
1340         if((tree = gettree(t->r->tid)) == nil){
1341                 t->respond(t, STATUS_SMB_BAD_TID);
1342                 goto out;
1343         }
1344         share = tree->share;
1345         if(debug)
1346                 fprint(2, "FS level %.4x\n", level);
1347         switch(level){
1348         case 0x0001:    /* SMB_INFO_ALLOCATION */
1349                 n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "llllw", 
1350                         0x00000000, share->blocksize/share->sectorsize,
1351                         filesize32(allocsize(share->allocsize+share->freesize, share->blocksize)/share->blocksize),
1352                         filesize32(allocsize(share->freesize, share->blocksize)/share->blocksize),
1353                         share->sectorsize);
1354                 break;
1355
1356         case 0x0002:    /* SMB_INFO_VOLUME */
1357                 s = smprint("%.12s", share->name);
1358                 n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "l#0b{f}",
1359                         (int)namehash(share->root), smbuntermstrpack8, s);
1360                 break;
1361
1362         case 0x0102:    /* SMB_QUERY_FS_VOLUME_INFO */
1363                 s = smprint("%.12s", share->name);
1364                 n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "vl#0l__{f}",
1365                         tofiletime(starttime), (int)namehash(share->root), smbuntermstrpack16, s);
1366                 break;
1367
1368         case 0x0103:    /* SMB_QUERY_FS_SIZE_INFO */
1369                 n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "vvll", 
1370                         allocsize(share->allocsize+share->freesize, share->blocksize)/share->blocksize,
1371                         allocsize(share->freesize, share->blocksize)/share->blocksize, 
1372                         share->blocksize/share->sectorsize,
1373                         share->sectorsize);
1374                 break;
1375
1376         case 0x0105:    /* SMB_QUERY_FS_ATTRIBUTE_INFO */
1377                 n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "ll#0l{f}", 
1378                         FILE_CASE_SENSITIVE_SEARCH |
1379                         FILE_CASE_PRESERVED_NAMES |
1380                         FILE_UNICODE_ON_DISK,
1381                         share->namelen, smbuntermstrpack16, share->fsname);
1382                 break;
1383
1384         default:
1385                 logit("[%.4x] unknown FS infolevel", level);
1386                 t->respond(t, STATUS_OS2_INVALID_LEVEL);
1387                 goto out;
1388         }
1389         if(n <= 0)
1390                 t->respond(t, STATUS_INVALID_SMB);
1391         else {
1392                 t->out.data.p += n;
1393                 t->respond(t, 0);
1394         }
1395 out:
1396         free(s);
1397 }
1398
1399 enum {
1400         SMB_FIND_CLOSE_AFTER_REQUEST = 0x1,
1401         SMB_FIND_CLOSE_AT_EOS = 0x2,
1402         SMB_FIND_RETURN_RESUME_KEYS = 0x4,
1403         SMB_FIND_CONTINUE_FROM_LAST = 0x8,
1404 };
1405
1406 void
1407 trans2findfirst2(Trans *t)
1408 {
1409         int i, nsid, eos, n, attr, count, flags, level;
1410         uchar *prevoff, *nameoff;
1411         char *name, *path;
1412         Tree *tree;
1413         Find *f;
1414         Dir *d;
1415
1416         f = nil;
1417         name = path = nil;
1418         if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "wwww____f", 
1419                 &attr, &count, &flags, &level, t->o->nameunpack, &name)){
1420                 t->respond(t, STATUS_NOT_SUPPORTED);
1421                 goto out;
1422         }
1423         if(debug)
1424                 fprint(2, "FIND level %.4x\n", level);
1425         if((path = getpath(t->r->tid, name, &tree, &n)) == nil){
1426                 t->respond(t, n);
1427                 goto out;
1428         }
1429         if((f = openfind(path, t->namecmp, attr, 1, &n)) == nil){
1430                 t->respond(t, n);
1431                 goto out;
1432         }
1433         n = eos = 0;
1434         prevoff = nameoff = nil;
1435         for(i = 0; i < count; i++){
1436                 if((eos = readfind(f, f->index, &d)) < 0)
1437                         break;
1438                 if((n = fpackdir(t->r, d, tree, 0, level,
1439                         t->out.data.b, t->out.data.p, t->out.data.e, 
1440                         &prevoff, &nameoff)) <= 0)
1441                         break;
1442                 t->out.data.p += n;
1443                 f->index = eos + 1;
1444         }
1445         if((n < 0) || (flags & SMB_FIND_CLOSE_AFTER_REQUEST) || 
1446            ((flags & SMB_FIND_CLOSE_AT_EOS) && (eos < 0))){
1447                 if(n < 0){
1448                         t->respond(t, STATUS_OS2_INVALID_LEVEL);
1449                         goto out;
1450                 }
1451                 eos = -1;
1452                 nsid = 0;
1453         } else {
1454                 nsid = newsid(tree, f);
1455         }
1456         if(!i && (eos < 0)){
1457                 t->respond(t, STATUS_NO_MORE_FILES);
1458                 goto out;
1459         }
1460         if(!pack(t->out.param.b, t->out.param.p, t->out.param.e, "wwwww.",
1461                 nsid, i, (eos < 0), 0, (int)(nameoff - t->out.data.b), &t->out.param.p)){
1462                 t->respond(t, STATUS_INVALID_SMB);
1463                 delsid(tree, nsid);
1464         } else
1465                 t->respond(t, 0);
1466 out:
1467         free(name);
1468         free(path);
1469         putfind(f);
1470 }
1471
1472 void
1473 trans2findnext2(Trans *t)
1474 {
1475         int i, n, eos, sid, count, level, index, flags;
1476         uchar *prevoff, *nameoff;
1477         char *name;
1478         Tree *tree;
1479         Find *f;
1480         Dir *d;
1481
1482         f = nil;
1483         name = nil;
1484         if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "wwwlwf", 
1485                 &sid, &count, &level, &index, &flags, t->o->nameunpack, &name)){
1486                 t->respond(t, STATUS_NOT_SUPPORTED);
1487                 goto out;
1488         }
1489         if(debug)
1490                 fprint(2, "FIND level %.4x\n", level);
1491         if((f = getfind(t->r->tid, sid, &tree, &n)) == nil){
1492                 t->respond(t, n);
1493                 goto out;
1494         }
1495         n = eos = 0;
1496         if((flags & SMB_FIND_CONTINUE_FROM_LAST) == 0){
1497                 f->index = 0;
1498                 while((eos = readfind(f, f->index, &d)) >= 0){
1499                         f->index = eos + 1;
1500                         if(strcmp(name, d->name) == 0)
1501                                 break;
1502                 }
1503         }
1504         prevoff = nameoff = nil;
1505         for(i = 0; i < count; i++){
1506                 if((eos = readfind(f, f->index, &d)) < 0)
1507                         break;
1508                 if((n = fpackdir(t->r, d, tree, 0, level,
1509                         t->out.data.b, t->out.data.p, t->out.data.e, 
1510                         &prevoff, &nameoff)) <= 0)
1511                         break;
1512                 t->out.data.p += n;
1513                 f->index = eos + 1;
1514         }
1515         if((flags & SMB_FIND_CLOSE_AFTER_REQUEST) || 
1516            ((flags & SMB_FIND_CLOSE_AT_EOS) && (eos < 0))){
1517                 delsid(tree, sid);
1518                 eos = -1;
1519         }
1520         if(!i && (eos < 0)){
1521                 t->respond(t, STATUS_NO_MORE_FILES);
1522                 goto out;
1523         }
1524         if(!pack(t->out.param.b, t->out.param.p, t->out.param.e, "wwww.",
1525                 i, (eos < 0), 0, (int)(nameoff - t->out.data.b), &t->out.param.p))
1526                 t->respond(t, STATUS_INVALID_SMB);
1527         else
1528                 t->respond(t, 0);
1529 out:
1530         free(name);
1531         putfind(f);
1532 }
1533
1534 static void
1535 transrespond(Trans *t, int err)
1536 {
1537         Req *r;
1538
1539         r = t->r;
1540         t->r = nil;
1541         t->respond = nil;
1542         if(!err && !pack(r->rh, r->rp, r->re, 
1543                 "#0b{*2ww__#3w@3ww#4w@4ww#1b_[*2]}#2w{%4[]%4[]}.",
1544                 t->out.param.p - t->out.param.b, 
1545                 t->out.data.p - t->out.data.b,
1546                 0, 0,
1547                 t->out.setup.b, t->out.setup.p,
1548                 t->out.param.b, t->out.param.p,
1549                 t->out.data.b, t->out.data.p, &r->rp))
1550                 r->respond(r, STATUS_INVALID_SMB);
1551         else
1552                 r->respond(r, err);
1553         free(t->out.param.b);
1554         free(t->out.data.b);
1555         free(t->out.setup.b);
1556 }
1557
1558 struct {
1559         char *name;
1560         void (*fun)(Trans *t);
1561 } transoptab[] = {
1562         [0x0000] { "TRANS_RAP", transrap },
1563 }, trans2optab[] = {
1564         [0x0001] { "TRANS2_FIND_FIRST2", trans2findfirst2 },
1565         [0x0002] { "TRANS2_FIND_NEXT2", trans2findnext2 },
1566         [0x0003] { "TRANS2_QUERY_FS_INFORMATION", trans2queryfsinformation },
1567         [0x0005] { "TRANS2_QUERY_PATH_INFORMATION", trans2querypathinformation },
1568         [0x0007] { "TRANS2_QUERY_FILE_INFORMATION", trans2queryfileinformation },
1569         [0x0006] { "TRANS2_SET_PATH_INFORMATION", trans2setpathinformation },
1570         [0x0008] { "TRANS2_SET_FILE_INFORMATION", trans2setfileinformation },
1571 };
1572
1573 void
1574 smbtransaction(Req *r, uchar *h, uchar *p, uchar *e)
1575 {
1576         int tpc, tdc, rpc, rdc, rsc;
1577         uchar *sa, *se, *da, *de, *pa, *pe;
1578         void (*fun)(Trans *t);
1579         Trans t;
1580
1581         t.r = r;
1582         t.o = r->o;
1583         t.namecmp = r->namecmp;
1584         t.cmd = 0;
1585         t.respond = transrespond;
1586         if(!unpack(h, p, e, "#0b{*2wwwwb_w______#3w@3w#4w@4w#1b_[*2]}#2w{[?][?]}",
1587                 &tpc, &tdc, &rpc, &rdc, &rsc, &t.flags, &sa, &se, &pa, &pe, &da, &de)){
1588 unsup:
1589                 r->respond(r, STATUS_NOT_SUPPORTED);
1590                 return;
1591         }
1592         unpack(sa, sa, se, "w", &t.cmd);
1593
1594         switch(r->cmd){
1595         case 0x25:      /* SMB_COM_TRANSACTION */
1596                 if((t.cmd >= nelem(transoptab)) || ((fun = transoptab[t.cmd].fun) == nil)){
1597                         logit("[%.4x] transaction subcommand not implemented", t.cmd);
1598                         goto unsup;
1599                 }
1600                 t.name = transoptab[t.cmd].name;
1601                 break;
1602         case 0x32:      /* SMB_COM_TRANSACTION2 */
1603                 if((t.cmd >= nelem(trans2optab)) || ((fun = trans2optab[t.cmd].fun) == nil)){
1604                         logit("[%.4x] transaction2 subcommand not implemented", t.cmd);
1605                         goto unsup;
1606                 }
1607                 t.name = trans2optab[t.cmd].name;
1608                 break;
1609         default:
1610                 goto unsup;
1611         }
1612
1613         if((tpc > (pe - pa)) || (tdc > (de - da))){
1614                 logit("[%.4x] %s request truncated", t.cmd, t.name);
1615                 goto unsup;
1616         }
1617         if(57+((rsc+1)&~1)+((rpc+3)&~3)+((rdc+3)&~3) > remotebuffersize){
1618                 rdc = remotebuffersize-(57+((rsc+1)&~1)+((rpc+3)&~3)) & ~3;
1619                 if(rdc <= 0){
1620                         logit("[%.4x] %s response doesnt fit in client buffer", t.cmd, t.name);
1621                         goto unsup;
1622                 }
1623         }
1624
1625         t.in.param.b = t.in.param.p = pa; t.in.param.e = pe;
1626         t.in.data.b = t.in.data.p = da; t.in.data.e = de;
1627         t.in.setup.b = t.in.setup.p = sa; t.in.setup.e = se;
1628
1629         t.out.param.b = t.out.param.p = t.out.param.e = (rpc > 0) ? malloc(rpc) : nil;
1630         t.out.param.e += rpc;
1631         t.out.data.b = t.out.data.p = t.out.data.e = (rdc > 0) ? malloc(rdc) : nil;
1632         t.out.data.e += rdc;
1633         t.out.setup.b = t.out.setup.p = t.out.setup.e = (rsc > 0) ? malloc(rsc) : nil;
1634         t.out.setup.e += rsc;
1635
1636         if(debug)
1637                 fprint(2, "[%.4x] %s\n", t.cmd, t.name);
1638         (*fun)(&t);
1639 }
1640
1641 void
1642 smbnoandxcommand(Req *r, uchar *, uchar *, uchar *)
1643 {
1644         r->respond(r, (r->cmd == 0xFF) ? STATUS_INVALID_SMB : 0);
1645 }
1646
1647 struct {
1648         char *name;
1649         void (*fun)(Req *, uchar *, uchar *, uchar *);
1650 } optab[] = {
1651         [0x00] { "SMB_COM_CREATE_DIRECTORY", smbcreatedirectory },
1652         [0x01] { "SMB_COM_DELETE_DIRECTORY", smbdeletedirectory },
1653         [0x04] { "SMB_COM_CLOSE", smbcloseflush },
1654         [0x05] { "SMB_COM_FLUSH", smbcloseflush },
1655         [0x06] { "SMB_COM_DELETE", smbdelete },
1656         [0x07] { "SMB_COM_RENAME", smbrename },
1657         [0x08] { "SMB_COM_QUERY_INFORMATION", smbqueryinformation },
1658         [0x09] { "SMB_COM_SET_INFORMATION", smbsetinformation },
1659         [0x10] { "SMB_CON_CHECK_DIRECTORY", smbcheckdirectory },
1660         [0x0b] { "SMB_COM_WRITE", smbwrite },
1661         [0x23] { "SMB_COM_QUERY_INFORMATION2", smbqueryinformation2 },
1662         [0x24] { "SMB_COM_LOCKING_ANDX", smblockingandx },
1663         [0x25] { "SMB_COM_TRANSACTION", smbtransaction },
1664         [0x2b] { "SMB_COM_ECHO", smbecho },
1665         [0x2d] { "SMB_COM_OPEN_ANDX", smbopenandx },
1666         [0x2e] { "SMB_COM_READ_ANDX", smbreadandx },
1667         [0x2f] { "SMB_COM_WRITE_ANDX", smbwriteandx },
1668         [0x32] { "SMB_COM_TRANSACTION2", smbtransaction },
1669         [0x34] { "SMB_COM_FIND_CLOSE2", smbcloseflush },
1670         [0x71] { "SMB_COM_DISCONNECT_TREE", smbdisconnecttree },
1671         [0x72] { "SMB_COM_NEGOTIATE", smbnegotiate },
1672         [0x73] { "SMB_COM_SESSION_SETUP_ANX", smbsessionsetupandx },
1673         [0x74] { "SMB_COM_LOGOFF_ANDX", smblogoffandx },
1674         [0x75] { "SMB_COM_TREE_CONNECT_ANDX", smbtreeconnectandx },
1675         [0x80] { "SMB_COM_QUERY_INFORMATION_DISK", smbqueryinformationdisk },
1676         [0xa2] { "SMB_COM_NT_CREATE_ANDX", smbntcreatendx },
1677         [0xFF] { "SMB_COM_NO_ANDX_COMMAND", smbnoandxcommand },
1678 };
1679
1680 void
1681 smbcmd(Req *r, int cmd, uchar *h, uchar *p, uchar *e)
1682 {
1683         if((cmd >= nelem(optab)) || (optab[cmd].fun == nil)){
1684                 logit("[%.2x] command not implemented", cmd);
1685                 r->respond(r, STATUS_NOT_SUPPORTED);
1686                 return;
1687         }
1688         r->name = optab[cmd].name;
1689         if(debug)
1690                 fprint(2, "[%.2x] %s\n", cmd, r->name);
1691         if((!negotiated && cmd != 0x72) || (negotiated && cmd == 0x72)){
1692                 r->respond(r, STATUS_INVALID_SMB);
1693                 return;
1694         }
1695         if(!remoteuser){
1696                 switch(cmd){
1697                 case 0x72: /* SMB_COM_NEGOTIATE */
1698                 case 0x73: /* SMB_COM_SESSION_SETUP_ANX */
1699                 case 0x74: /* SMB_COM_LOGOFF_ANDX */
1700                 case 0xFF: /* SMB_COM_NO_ANDX_COMMAND */
1701                         break;
1702                 default:
1703                         logit("auth not completed in %s request", r->name);
1704                 case 0x75: /* SMB_COM_TREE_CONNECT_ANDX */
1705                         r->respond(r, STATUS_LOGON_FAILURE);
1706                         return;
1707                 }
1708         } else if(r->uid != sessionuid){
1709                 switch(cmd){
1710                 case 0x73: /* SMB_COM_SESSION_SETUP_ANX */
1711                 case 0x2b: /* SMB_COM_ECHO */
1712                         break;
1713                 default:
1714                         logit("bad uid %.4x in %s request", r->uid, r->name);
1715                         r->respond(r, STATUS_SMB_BAD_UID);
1716                         return;
1717                 }
1718         }
1719         (*optab[cmd].fun)(r, h, p, e);
1720 }