]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/aux/depend.c
kbdfs: allow to escape ctlr-alt-del with shift for vmx and vnc.
[plan9front.git] / sys / src / cmd / aux / depend.c
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include <thread.h>
6 #include <bio.h>
7
8 typedef struct Args Args;
9
10 struct Args {
11         int     argc;
12         char    **argv;
13 };
14
15 typedef struct Dfile Dfile;
16 typedef struct Fid Fid;
17 typedef struct File File;
18 typedef struct Fs Fs;
19 typedef struct Request Request;
20 typedef struct Symbol Symbol;
21 typedef struct Tardir Tardir;
22
23 extern int threadrforkflag = RFNAMEG;
24
25 enum{
26         Nstat = 1024,   /* plenty for this application */
27         MAXSIZE = 8192+IOHDRSZ,
28 };
29
30 int messagesize = MAXSIZE;
31
32 void
33 fatal(char *fmt, ...)
34 {
35         va_list arg;
36         char buf[1024];
37
38         write(2, "depend: ", 8);
39         va_start(arg, fmt);
40         vseprint(buf, buf+1024, fmt, arg);
41         va_end(arg);
42         write(2, buf, strlen(buf));
43         write(2, "\n", 1);
44         threadexitsall(fmt);
45 }
46
47 enum
48 {
49         Nfidhash=       64,
50         Ndfhash=        128,
51 };
52
53 struct Symbol
54 {
55         Symbol  *next;          /* hash list chaining */
56         char    *sym;
57         int     fno;            /* file symbol is defined in */
58 };
59
60 /* source file */
61 struct File
62 {
63         QLock;
64
65         char    *name;
66         Symbol  *ref;
67         uchar   *refvec;        /* files resolving the references */
68         uint    len;            /* length of file */
69         uint    tarlen;         /* length of tar file */
70         uint    mode;
71         uint    mtime;
72
73         int     use;
74         int     fd;
75 };
76
77 /* .depend file */
78 struct Dfile
79 {
80         Lock;
81         int     use;            /* use count */
82         int     old;            /* true if this is an superceded dfile */
83
84         File    *file;          /* files */
85         int     nfile;          /* number of files */
86         int     flen;           /* length of file table */
87
88         Symbol  **dhash;        /* hash table of symbols */
89         int     hlen;           /* length of hash table */
90
91         Dfile   *next;          /* hash chain */
92         char    *path;          /* path name of dependency file */
93         Qid     qid;            /* qid of the dependency file */
94 };
95
96 struct Fid
97 {
98         Fid     *next;
99         int     fid;
100         int     ref;
101
102         int     attached;
103         int     open;
104         Qid     qid;
105         char    *path;
106         Dfile   *df;
107         Symbol  *dp;
108         int     fd;
109         Dir     *dir;
110         int     ndir;
111         int     dirindex;
112 };
113
114 struct Request
115 {
116         Request *next;
117         Fid     *fid;
118         Fcall   f;
119         uchar   buf[1];
120 };
121
122 enum
123 {
124         Tblocksize=     512,    /* tar block size */
125         Tnamesize=      100,    /* tar name size */
126 };
127
128 struct Tardir
129 {
130         char    name[Tnamesize];
131         char    mode[8];
132         char    uid[8];
133         char    gid[8];
134         char    size[12];
135         char    mtime[12];
136         char    chksum[8];
137         char    linkflag;
138         char    linkname[Tnamesize];
139 };
140
141 struct Fs
142 {
143         Lock;
144
145         int     fd;             /* to kernel mount point */
146         Fid     *hash[Nfidhash];
147         char    *root;
148         Qid     rootqid;
149
150 };
151
152 struct Fsarg
153 {
154         Fs      *fs;
155         int     fd;
156         char *root;
157 };
158
159
160 extern  void    fsrun(void*);
161 extern  Fid*    fsgetfid(Fs*, int);
162 extern  void    fsputfid(Fs*, Fid*);
163 extern  void    fsreply(Fs*, Request*, char*);
164 extern  void    fsversion(Fs*, Request*, Fid*);
165 extern  void    fsauth(Fs*, Request*, Fid*);
166 extern  void    fsflush(Fs*, Request*, Fid*);
167 extern  void    fsattach(Fs*, Request*, Fid*);
168 extern  void    fswalk(Fs*, Request*, Fid*);
169 extern  void    fsopen(Fs*, Request*, Fid*);
170 extern  void    fscreate(Fs*, Request*, Fid*);
171 extern  void    fsread(Fs*, Request*, Fid*);
172 extern  void    fswrite(Fs*, Request*, Fid*);
173 extern  void    fsclunk(Fs*, Request*, Fid*);
174 extern  void    fsremove(Fs*, Request*, Fid*);
175 extern  void    fsstat(Fs*, Request*, Fid*);
176 extern  void    fswstat(Fs*, Request*, Fid*);
177
178 void    (*fcall[])(Fs*, Request*, Fid*) =
179 {
180         [Tflush]        fsflush,
181         [Tversion]      fsversion,
182         [Tauth] fsauth,
183         [Tattach]       fsattach,
184         [Twalk]         fswalk,
185         [Topen]         fsopen,
186         [Tcreate]       fscreate,
187         [Tread]         fsread,
188         [Twrite]        fswrite,
189         [Tclunk]        fsclunk,
190         [Tremove]       fsremove,
191         [Tstat]         fsstat,
192         [Twstat]        fswstat
193 };
194
195 char Eperm[]   = "permission denied";
196 char Eexist[]  = "file does not exist";
197 char Enotdir[] = "not a directory";
198 char Eisopen[] = "file already open";
199 char Enofid[] = "no such fid";
200 char mallocerr[]        = "malloc: %r";
201 char Etoolong[] = "name too long";
202
203 char *dependlog = "depend";
204
205 int debug;
206 Dfile *dfhash[Ndfhash];         /* dependency file hash */
207 QLock dfhlock[Ndfhash];
208 QLock iolock;
209
210 Request*        allocreq(int);
211 Dfile*  getdf(char*);
212 void    releasedf(Dfile*);
213 Symbol* dfsearch(Dfile*, char*);
214 void    dfresolve(Dfile*, int);
215 char*   mkpath(char*, char*);
216 int     mktar(Dfile*, Symbol*, uchar*, uint, int);
217 void    closetar(Dfile*, Symbol*);
218
219 void*
220 emalloc(uint n)
221 {
222         void *p;
223
224         p = malloc(n);
225         if(p == nil)
226                 fatal(mallocerr);
227         memset(p, 0, n);
228         return p;
229 }
230
231 void *
232 erealloc(void *ReallocP, int ReallocN)
233 {
234         if(ReallocN == 0)
235                 ReallocN = 1;
236         if(!ReallocP)
237                 ReallocP = emalloc(ReallocN);
238         else if(!(ReallocP = realloc(ReallocP, ReallocN)))
239                 fatal("unable to allocate %d bytes",ReallocN);
240         return(ReallocP);
241 }
242
243 char*
244 estrdup(char *s)
245 {
246         char *d, *d0;
247
248         if(!s)
249                 return 0;
250         d = d0 = emalloc(strlen(s)+1);
251         while(*d++ = *s++)
252                 ;
253         return d0;
254 }
255
256 /*
257  *  mount the user interface and start one request processor
258  *  per CPU
259  */
260 void
261 realmain(void *a)
262 {
263         Fs *fs;
264         int pfd[2];
265         int srv;
266         char service[128];
267         struct Fsarg fsarg;
268         Args *args;
269         int argc;
270         char **argv;
271
272         args = a;
273         argc = args->argc;
274         argv = args->argv;
275
276         fmtinstall('F', fcallfmt);
277
278         ARGBEGIN{
279                 case 'd':
280                         debug++;
281                         break;
282         }ARGEND
283         if(argc != 2){
284                 fprint(2, "usage: %s [-d] svc-name directory\n", argv0);
285                 exits("usage");
286         }
287         snprint(service, sizeof service, "#s/%s", argv[0]);
288         if(argv[1][0] != '/')
289                 fatal("directory must be rooted");
290
291         if(pipe(pfd) < 0)
292                 fatal("opening pipe: %r");
293
294         /* Typically mounted before /srv exists */
295         srv = create(service, OWRITE, 0666);
296         if(srv < 0)
297                 fatal("post: %r");
298         fprint(srv, "%d", pfd[1]);
299         close(srv);
300         close(pfd[1]);
301
302         time(nil);      /* open fd for time before losing / */
303         if(bind(argv[1], "/", MREPL) == -1)
304                 fatal("can't bind %s to /", argv[1]);
305
306         fs = emalloc(sizeof(Fs));
307         fsarg.fs = fs;
308         fsarg.fd = pfd[0];
309         fsarg.root = argv[1];
310         proccreate(fsrun, &fsarg, 16*1024);
311         proccreate(fsrun, &fsarg, 16*1024);
312         fsrun(&fsarg);
313         exits(nil);
314 }
315
316 void
317 threadmain(int argc, char *argv[])
318 {
319         static Args args;
320
321         args.argc = argc;
322         args.argv = argv;
323         rfork(RFNAMEG);
324         proccreate(realmain, &args, 16*1024);
325 }
326
327 char*
328 mkpath(char *dir, char *file)
329 {
330         int len;
331         char *path;
332
333         len = strlen(dir) + 1;
334         if(file != nil)
335                 len += strlen(file) + 1;
336         path = emalloc(len);
337         if(file != nil)
338                 sprint(path, "%s/%s", dir, file);
339         else
340                 sprint(path, "%s", dir);
341         return path;
342 }
343
344 void
345 fsrun(void *a)
346 {
347         struct Fsarg *fsarg;
348         Fs* fs;
349         char *root;
350         int n, t;
351         Request *r;
352         Fid *f;
353         Dir *d;
354
355         fsarg = a;
356         fs = fsarg->fs;
357         fs->fd = fsarg->fd;
358         root = fsarg->root;
359         d = dirstat("/");
360         if(d == nil)
361                 fatal("root %s inaccessible: %r", root);
362         fs->rootqid = d->qid;
363         free(d);
364
365         for(;;){
366                 r = allocreq(messagesize);
367                 qlock(&iolock);
368                 n = read9pmsg(fs->fd, r->buf, messagesize);
369                 if(n == 0)
370                         threadexitsall("unmounted");
371                 if(n < 0)
372                         fatal("mount read: %r");
373                 if(convM2S(r->buf, n, &r->f) != n)
374                         fatal("convM2S format error: %r");
375                 qunlock(&iolock);
376
377                 f = fsgetfid(fs, r->f.fid);
378                 r->fid = f;
379                 if(debug)
380                         fprint(2, "%F path %llux\n", &r->f, f->qid.path);
381
382                 t = r->f.type;
383                 r->f.type++;
384                 (*fcall[t])(fs, r, f);
385                 fsputfid(fs, f);
386         }
387 }
388
389 /*
390  *  any request that can get queued for a delayed reply
391  */
392 Request*
393 allocreq(int bufsize)
394 {
395         Request *r;
396
397         r = emalloc(sizeof(Request)+bufsize);
398         r->next = nil;
399         return r;
400 }
401
402 Fid*
403 fsgetfid(Fs *fs, int fid)
404 {
405         Fid *f, *nf;
406
407         lock(fs);
408         for(f = fs->hash[fid%Nfidhash]; f; f = f->next){
409                 if(f->fid == fid){
410                         f->ref++;
411                         unlock(fs);
412                         return f;
413                 }
414         }
415
416         nf = emalloc(sizeof(Fid));
417         nf->next = fs->hash[fid%Nfidhash];
418         fs->hash[fid%Nfidhash] = nf;
419         nf->fid = fid;
420         nf->ref = 1;
421         nf->fd = -1;
422         unlock(fs);
423         return nf;
424 }
425
426 void
427 fsputfid(Fs *fs, Fid *f)
428 {
429         Fid **l, *nf;
430
431         lock(fs);
432         if(--f->ref > 0){
433                 unlock(fs);
434                 return;
435         }
436         for(l = &fs->hash[f->fid%Nfidhash]; nf = *l; l = &nf->next)
437                 if(nf == f){
438                         *l = f->next;
439                         break;
440                 }
441         unlock(fs);
442         free(f);
443 }
444
445 void
446 fsreply(Fs *fs, Request *r, char *err)
447 {
448         int n;
449         uchar buf[MAXSIZE];
450
451         if(err){
452                 r->f.type = Rerror;
453                 r->f.ename = err;
454         }
455         if(debug)
456                 fprint(2, "%F path %llux\n", &r->f, r->fid->qid.path);
457         n = convS2M(&r->f, buf, messagesize);
458         if(n == 0)
459                 fatal("bad convS2M");
460         if(write(fs->fd, buf, n) != n)
461                 fatal("unmounted");
462         free(r);
463 }
464
465 void
466 fsversion(Fs *fs, Request *r, Fid*)
467 {
468         if(r->f.msize < 256){
469                 fsreply(fs, r, "version: bad message size");
470                 return;
471         }
472         if(messagesize > r->f.msize)
473                 messagesize = r->f.msize;
474         r->f.msize = messagesize;
475         if(strncmp(r->f.version, "9P", 2) != 0)
476                 r->f.version = "unknown";
477         else
478                 r->f.version = "9P2000";
479         fsreply(fs, r, nil);
480 }
481
482 void
483 fsauth(Fs *fs, Request *r, Fid*)
484 {
485         fsreply(fs, r, "depend: authentication not required");
486 }
487
488 void
489 fsflush(Fs*, Request*, Fid*)
490 {
491 }
492
493 void
494 fsattach(Fs *fs, Request *r, Fid *f)
495 {
496         f->qid = fs->rootqid;
497         f->path = strdup("/");
498         f->df = getdf(mkpath(f->path, ".depend"));
499
500         /* hold down the fid till the clunk */
501         f->attached = 1;
502         lock(fs);
503         f->ref++;
504         unlock(fs);
505
506         r->f.qid = f->qid;
507         fsreply(fs, r, nil);
508 }
509
510 void
511 fswalk(Fs *fs, Request *r, Fid *f)
512 {
513         Fid *nf;
514         char *name, *tmp;
515         int i, nqid, nwname;
516         char errbuf[ERRMAX], *err;
517         Qid qid[MAXWELEM];
518         Dfile *lastdf;
519         char *path, *npath;
520         Dir *d;
521         Symbol *dp;
522
523         if(f->attached == 0){
524                 fsreply(fs, r, Eexist);
525                 return;
526         }
527
528         if(f->fd >= 0 || f->open)
529                 fatal("walk of an open file");
530
531         nf = nil;
532         if(r->f.newfid != r->f.fid){
533                 nf = fsgetfid(fs, r->f.newfid);
534                 nf->attached = 1;
535                 nf->open = f->open;
536                 nf->path = strdup(f->path);
537                 nf->qid = f->qid;
538                 nf->dp = f->dp;
539                 nf->fd = f->fd;
540                 nf->df = f->df;
541                 if(nf->df){
542                         lock(nf->df);
543                         nf->df->use++;
544                         unlock(nf->df);
545                 }
546                 if(r->f.nwname == 0){
547                         r->f.nwqid = 0;
548                         fsreply(fs, r, nil);
549                         return;
550                 }
551                 f = nf;
552         }
553
554         err = nil;
555         path = strdup(f->path);
556         if(path == nil)
557                 fatal(mallocerr);
558         nqid = 0;
559         nwname = r->f.nwname;
560         lastdf = f->df;
561
562         if(nwname > 0){
563                 for(; nqid<nwname; nqid++){
564                         name = r->f.wname[nqid];
565
566                         if(strcmp(name, ".") == 0){
567         Noop:
568                                 if(nqid == 0)
569                                         qid[nqid] = f->qid;
570                                 else
571                                         qid[nqid] = qid[nqid-1];
572                                 continue;
573                         }
574
575                         if(strcmp(name, "..") == 0){
576                                 name = strrchr(path, '/');
577                                 if(name){
578                                         if(name == path)        /* at root */
579                                                 goto Noop;
580                                         *name = '\0';
581                                 }
582                                 d = dirstat(path);
583                                 if(d == nil){
584                                         *name = '/';
585                                         errstr(errbuf, sizeof errbuf);
586                                         err = errbuf;
587                                         break;
588                                 }
589         Directory:
590                                 qid[nqid] = d->qid;
591                                 free(d);
592                                 releasedf(lastdf);
593                                 lastdf = getdf(mkpath(path, ".depend"));
594                                 continue;
595                         }
596
597                         npath = mkpath(path, name);
598                         free(path);
599                         path = npath;
600                         d = dirstat(path);
601
602                         if(d !=nil && (d->qid.type & QTDIR))
603                                 goto Directory;
604                         free(d);
605
606                         qid[nqid].type = QTFILE;
607                         qid[nqid].path = 0;
608                         qid[nqid].vers = 0;
609
610                         dp = dfsearch(lastdf, name);
611                         if(dp == nil){
612                                 tmp = strdup(name);
613                                 if(tmp == nil)
614                                         fatal("mallocerr");
615                                 i = strlen(tmp);
616                                 if(i > 4 && strcmp(&tmp[i-4], ".tar") == 0){
617                                         tmp[i-4] = 0;
618                                         dp = dfsearch(lastdf, tmp);
619                                 }
620                                 free(tmp);
621                         }
622
623                         if(dp == nil){
624                                 err = Eexist;
625                                 break;
626                         }
627                         qid[nqid].path = (uvlong)dp;
628                         qid[nqid].vers = 0;
629                 }
630                 if(nqid == 0 && err == nil)
631                         err = "file does not exist";
632         }
633
634         /* for error or partial success, put the cloned fid back*/
635         if(nf!=nil && (err != nil || nqid < nwname)){
636                 releasedf(nf->df);
637                 nf->df = nil;
638                 fsputfid(fs, nf);
639         }
640
641         if(err == nil){
642                 /* return (possibly short) list of qids */
643                 for(i=0; i<nqid; i++)
644                         r->f.wqid[i] = qid[i];
645                 r->f.nwqid = nqid;
646
647                 /* for full success, advance f */
648                 if(nqid > 0 && nqid == nwname){
649                         free(f->path);
650                         f->path = path;
651                         path = nil;
652
653                         f->qid = qid[nqid-1];
654                         f->dp = (Symbol*)f->qid.path;
655
656                         if(f->df != lastdf){
657                                 releasedf(f->df);
658                                 f->df = lastdf;
659                                 lastdf = nil;
660                         }
661
662                 }
663         }
664
665         releasedf(lastdf);
666         free(path);
667
668         fsreply(fs, r, err);
669 }
670
671 #ifdef adf
672 void
673 fsclone(Fs *fs, Request *r, Fid *f)
674 {
675         Fid *nf;
676
677         if(f->attached == 0){
678                 fsreply(fs, r, Eexist);
679                 return;
680         }
681         nf = fsgetfid(fs, r->f.newfid);
682
683         nf->attached = 1;
684         nf->open = f->open;
685         nf->path = strdup(f->path);
686         nf->qid = f->qid;
687         nf->dp = f->dp;
688         nf->fd = f->fd;
689         nf->df = f->df;
690         if(nf->df){
691                 lock(nf->df);
692                 nf->df->use++;
693                 unlock(nf->df);
694         }
695         fsreply(fs, r, nil);
696 }
697
698 void
699 fswalk(Fs *fs, Request *r, Fid *f)
700 {
701         char *name;
702         int i;
703         Dir d;
704         char errbuf[ERRLEN];
705         char *path;
706         Symbol *dp;
707
708         if(f->attached == 0){
709                 fsreply(fs, r, Enofid);
710                 return;
711         }
712
713         if(f->fd >= 0 || f->open)
714                 fatal("walk of an open file");
715
716         name = r->f.name;
717         if(strcmp(name, ".") == 0){
718                 fsreply(fs, r, nil);
719                 return;
720         }
721         if(strcmp(name, "..") == 0){
722                 name = strrchr(f->path, '/');
723                 if(name){
724                         if(name == f->path){
725                                 fsreply(fs, r, nil);
726                                 return;
727                         }
728                         *name = 0;
729                 }
730                 if(dirstat(f->path, &d) < 0){
731                         *name = '/';
732                         errstr(errbuf);
733                         fsreply(fs, r, errbuf);
734                         return;
735                 }
736                 r->f.qid = f->qid = d.qid;
737
738                 releasedf(f->df);
739                 f->df = getdf(mkpath(f->path, ".depend"));
740
741                 fsreply(fs, r, nil);
742                 return;
743         }
744
745         path = mkpath(f->path, name);
746         if(dirstat(path, &d) < 0 || (d.qid.path & CHDIR) == 0){
747                 dp = dfsearch(f->df, name);
748                 if(dp == nil){
749                         i = strlen(name);
750                         if(i > 4 && strcmp(&name[i-4], ".tar") == 0){
751                                 name[i-4] = 0;
752                                 dp = dfsearch(f->df, name);
753                         }
754                 }
755                 if(dp == nil){
756                         fsreply(fs, r, Eexist);
757                         free(path);
758                         return;
759                 }
760                 f->dp = dp;
761                 d.qid.path = (uint)dp;
762                 d.qid.vers = 0;
763         }
764
765         free(f->path);
766         f->path = path;
767
768         if(d.qid.path & CHDIR){
769                 releasedf(f->df);
770                 f->df = getdf(mkpath(f->path, ".depend"));
771         }
772
773         r->f.qid = f->qid = d.qid;
774         fsreply(fs, r, nil);
775 }
776 #endif
777 void
778 fsopen(Fs *fs, Request *r, Fid *f)
779 {
780         int mode;
781         char errbuf[ERRMAX];
782         
783         if(f->attached == 0){
784                 fsreply(fs, r, Enofid);
785                 return;
786         }
787         if(f->open){
788                 fsreply(fs, r, Eisopen);
789                 return;
790         }
791
792         mode = r->f.mode & 3;
793         if(mode != OREAD){
794                 fsreply(fs, r, Eperm);
795                 return;
796         }
797
798         if(f->qid.type & QTDIR){
799                 f->fd = open(f->path, OREAD);
800                 if(f->fd < 0){
801                         errstr(errbuf, sizeof errbuf);
802                         fsreply(fs, r, errbuf);
803                         return;
804                 }
805         }
806
807         f->open = 1;
808         r->f.qid = f->qid;
809         fsreply(fs, r, nil);
810 }
811
812 void
813 fscreate(Fs *fs, Request *r, Fid*)
814 {
815         fsreply(fs, r, Eperm);
816 }
817
818 void
819 fsread(Fs *fs, Request *r, Fid *f)
820 {
821         int i, n, len,skip;
822         Dir d;
823         Symbol *dp;
824         char buf[512];
825
826         if(f->attached == 0){
827                 fsreply(fs, r, Enofid);
828                 return;
829         }
830         if((int)r->f.count < 0){
831                 fsreply(fs, r, "bad read count");
832                 return;
833         }
834
835         if(f->qid.type & QTDIR){
836                 n = 0;
837                 if(f->dir == nil){
838                         f->ndir = dirreadall(f->fd, &f->dir);
839                         f->dirindex = 0;
840                 }
841                 if(f->dir == nil)
842                         goto Return;
843                 if(r->f.offset == 0)    /* seeking to zero is permitted */
844                         f->dirindex = 0;
845                 for(; f->dirindex < f->ndir; f->dirindex++){
846                         if((f->dir[f->dirindex].qid.type & QTDIR) == 0)
847                                 continue;
848                         len = convD2M(&f->dir[f->dirindex], r->buf+n, r->f.count-n);
849                         if(len <= BIT16SZ)
850                                 goto Return;
851                         n += len;
852                 }
853
854                 skip = f->dirindex - f->ndir;   /* # depend records already read */
855
856                 if(f->df){
857                         for(i = 0; i < f->df->hlen; i++)
858                                 for(dp = f->df->dhash[i]; dp; dp = dp->next){
859                                         if(skip-- > 0)
860                                                 continue;
861                                         snprint(buf, sizeof buf, "%s.tar", dp->sym);
862                                         d.name = buf;
863                                         d.uid = "none";
864                                         d.gid = "none";
865                                         d.muid = "none";
866                                         d.qid.type = QTFILE;
867                                         d.qid.path = (uvlong)dp;
868                                         d.qid.vers = 0;
869                                         d.length = f->df->file[dp->fno].tarlen;
870                                         d.mode = 0444;
871                                         d.mtime = time(nil);
872                                         d.atime = time(nil);
873                                         len = convD2M(&d, r->buf + n, r->f.count - n);
874                                         if(len <= BIT16SZ)
875                                                 goto Return;
876                                         n += len;
877                                         f->dirindex++;
878                                 }
879                 }
880         } else
881                 n = mktar(f->df, f->dp, r->buf, r->f.offset, r->f.count);
882
883     Return:
884         r->f.data = (char*)r->buf;
885         r->f.count = n;
886         fsreply(fs, r, nil);
887 }
888
889 void
890 fswrite(Fs *fs, Request *r, Fid*)
891 {
892         fsreply(fs, r, Eperm);
893 }
894
895 void
896 fsclunk(Fs *fs, Request *r, Fid *f)
897 {
898         if(f->attached == 0){
899                 fsreply(fs, r, Enofid);
900                 return;
901         }
902         if(f->fd >= 0){
903                 close(f->fd);
904                 f->fd = -1;
905         }
906
907         if((f->qid.type & QTDIR) == 0)
908                 closetar(f->df, f->dp);
909
910         releasedf(f->df);
911         f->df = nil;
912         free(f->dir);
913         f->dir = nil;
914
915         fsreply(fs, r, nil);
916         fsputfid(fs, f);
917 }
918
919 void
920 fsremove(Fs *fs, Request *r, Fid*)
921 {
922         fsreply(fs, r, Eperm);
923 }
924
925 void
926 fsstat(Fs *fs, Request *r, Fid *f)
927 {
928         char err[ERRMAX];
929         Dir d;
930         Symbol *dp;
931         int n;
932         uchar statbuf[Nstat];
933
934         if(f->qid.type & QTDIR)
935                 n = stat(f->path, statbuf, sizeof statbuf);
936         else {
937                 dp = f->dp;
938                 d.name = dp->sym;
939                 d.uid = "none";
940                 d.gid = "none";
941                 d.muid = "none";
942                 d.qid.type = QTFILE;
943                 d.qid.path = (uvlong)dp;
944                 d.qid.vers = 0;
945                 d.length = f->df->file[dp->fno].tarlen;
946                 d.mode = 0444;
947                 d.mtime = time(nil);
948                 d.atime = time(nil);
949                 n = convD2M(&d, statbuf, sizeof statbuf);
950         }
951         if(n <= BIT16SZ){
952                 errstr(err, sizeof err);
953                 fsreply(fs, r, err);
954         } else {
955                 r->f.stat = statbuf;
956                 r->f.nstat = n;
957                 fsreply(fs, r, nil);
958         }
959 }
960
961 void
962 fswstat(Fs *fs, Request *r, Fid*)
963 {
964         fsreply(fs, r, Eperm);
965 }
966
967 /*
968  *  string hash
969  */
970 uint
971 shash(char *str, int len)
972 {
973         uint    hash;
974         char    *val; 
975
976         hash = 0;
977         for(val = str; *val; val++)
978                 hash = (hash*13) + *val-'a';
979         return hash % len;
980 }
981
982 /*
983  *  free info about a dependency file
984  */
985 void
986 freedf(Dfile *df)
987 {
988         int i;
989         Symbol *dp, *next;
990
991         lock(df);
992         df->old = 1;
993         if(df->use){
994                 unlock(df);
995                 return;
996         }
997
998         unlock(df);     /* we're no longer referenced */
999         for(i = 0; i < df->nfile; i++)
1000                 free(df->file[i].name);
1001         free(df->file[i].refvec);
1002         free(df->file);
1003         df->file = nil;
1004
1005         for(i = 0; i < df->hlen; i++)
1006                 for(dp = df->dhash[i]; dp != nil; dp = next){
1007                         next = dp->next;
1008                         free(dp);
1009                 }
1010
1011         free(df->path);
1012         free(df);
1013         free(df->dhash);
1014         df->dhash = nil;
1015 }
1016
1017 /*
1018  *  crack a dependency file
1019  */
1020 void
1021 newsym(char *name, int fno, Symbol **l)
1022 {
1023         Symbol *dp;
1024
1025         dp = emalloc(sizeof(Symbol));
1026         dp->sym = strdup(name);
1027         if(dp->sym == nil)
1028                 fatal("mallocerr");
1029         dp->next = *l;
1030         dp->fno = fno;
1031         *l = dp;
1032 }
1033 int
1034 awk(Biobuf *b, char **field, int n)
1035 {
1036         char *line;
1037         int i;
1038
1039         while(line = Brdline(b, '\n')){
1040                 line[Blinelen(b)-1] = 0;
1041                 while(*line == ' ' || *line == '\t')
1042                         *line++ = 0;
1043                 for(i = 0; i < n; i++){
1044                         if(*line == 0 || *line == '#')
1045                                 break;
1046                         field[i] = line;
1047                         while(*line && *line != ' ' && *line != '\t')
1048                                 line++;
1049                         while(*line == ' ' || *line == '\t')
1050                                 *line++ = 0;
1051                 }
1052                 if(i)
1053                         return i;
1054         }
1055
1056         return 0;
1057 }
1058
1059 void
1060 crackdf(Dfile *df, Biobuf *b, uint len, char *dpath)
1061 {
1062         char *name;
1063         char *field[3];
1064         char path[512];
1065         int n, inc, ok, npath;
1066         Symbol **l, *dp, *next;
1067         File *f, *ef;
1068         Dir *d;
1069
1070         inc = 32;
1071         df->flen = inc;
1072         df->file = emalloc(df->flen*sizeof(File));
1073         df->nfile = 0;
1074
1075         df->hlen = 1 + len/8;
1076         df->dhash = emalloc(df->hlen*sizeof(Symbol*));
1077
1078         l = nil;
1079         while((n = awk(b, field, 3)) > 0){
1080                 if(n != 2)
1081                         continue;
1082
1083                 name = field[1];
1084                 switch(*field[0]){
1085                 case 'F':
1086                         if(df->flen == df->nfile){
1087                                 df->flen += inc;
1088                                 df->file = realloc(df->file, df->flen*sizeof(File));
1089                                 if(df->file == nil)
1090                                         fatal(mallocerr);
1091                                 memset(&df->file[df->nfile], 0, inc*sizeof(File));
1092                         }
1093                         f = &df->file[df->nfile++];
1094                         f->name = strdup(name);
1095                         l = &f->ref;
1096                         /* fall through and define as a symbol */
1097                 case 'D':
1098                         if(l == nil)
1099                                 continue;
1100                         newsym(name, df->nfile-1, &(df->dhash[shash(name, df->hlen)]));
1101                         break;
1102                 case 'R':
1103                         if(l == nil)
1104                                 continue;
1105                         newsym(name, 0, l);
1106                         break;
1107                 }
1108         }
1109
1110         ef = &df->file[df->nfile];
1111
1112         /* stat the files to get sizes */
1113         npath = strlen(dpath);
1114         if(npath+1+1 >= sizeof path)
1115                 fatal(Etoolong);
1116         memmove(path, dpath, npath+1);  /* include NUL */
1117         name = strrchr(path, '/') + 1;
1118         for(f = df->file; f < ef; f++){
1119                 n = strlen(f->name);
1120                 if(npath+1+n+3+1 > sizeof path)
1121                         fatal(Etoolong);
1122                 memmove(name, f->name, n+1);    /* include NUL */
1123                 ok = access(path, AEXIST);
1124                 if(ok < 0){
1125                         strcpy(name+n, ".Z");
1126                         ok = access(path, AEXIST);
1127                         if(ok < 0){
1128                                 strcpy(name+n, ".gz");
1129                                 ok = access(path, AEXIST);
1130                         }
1131                 }
1132                 if(ok >= 0){
1133                         free(f->name);
1134                         f->name = strdup(name);
1135                         if(f->name == nil)
1136                                 fatal(mallocerr);
1137                 }
1138                 d = dirstat(path);
1139                 if(d){
1140                         f->len = d->length;
1141                         f->mode = d->mode;
1142                         f->mtime = d->mtime;
1143                         free(d);
1144                 }else{
1145                         f->len = 0;
1146                         f->mode = 0;
1147                         f->mtime = 0;
1148                 }
1149                 f->fd = -1;
1150         }
1151
1152         /* resolve all file references */
1153         for(f = df->file; f < ef; f++)
1154                 dfresolve(df, f-df->file);
1155
1156         /* free the referenced symbols, don't need them anymore */
1157         for(f = df->file; f < ef; f++){
1158                 f->tarlen += 2*Tblocksize;      /* tars trailing 0 blocks */
1159                 for(dp = f->ref; dp != nil; dp = next){
1160                         next = dp->next;
1161                         free(dp);
1162                 }
1163                 f->ref = nil;
1164         }
1165 }
1166
1167 /*
1168  *  get a cracked dependency file
1169  */
1170 Dfile*
1171 getdf(char *path)
1172 {
1173         Dfile *df, **l;
1174         QLock *lk;
1175         Dir *d;
1176         int i, fd;
1177         Biobuf *b;
1178
1179         i = shash(path, Ndfhash);
1180         l = &dfhash[i];
1181         lk = &dfhlock[i];
1182         qlock(lk);
1183         for(df = *l; df; df = *l){
1184                 if(strcmp(path, df->path) == 0)
1185                         break;
1186                 l = &df->next;
1187         }
1188         d = dirstat(path);
1189
1190         if(df){
1191                 if(d!=nil && d->qid.type == df->qid.type && d->qid.vers == df->qid.vers && d->qid.vers == df->qid.vers){
1192                         free(path);
1193                         lock(df);
1194                         df->use++;
1195                         unlock(df);
1196                         goto Return;
1197                 }
1198                 *l = df->next;
1199                 freedf(df);
1200         }
1201
1202         fd = open(path, OREAD);
1203         if(d == nil || fd < 0){
1204                 close(fd);
1205                 goto Return;
1206         }
1207
1208         df = emalloc(sizeof(*df));
1209         b = emalloc(sizeof(Biobuf));
1210
1211         Binit(b, fd, OREAD);
1212         df->qid = d->qid;
1213         df->path = path;
1214         crackdf(df, b, d->length, path);
1215         Bterm(b);
1216
1217         free(b);
1218
1219         df->next = *l;
1220         *l = df;
1221         df->use = 1;
1222     Return:
1223         qunlock(lk);
1224         free(d);
1225         return df;
1226 }
1227
1228 /*
1229  *  stop using a dependency file.  Free it if it is no longer linked in.
1230  */
1231 void
1232 releasedf(Dfile *df)
1233 {
1234         Dfile **l, *d;
1235         QLock *lk;
1236         int i;
1237
1238         if(df == nil)
1239                 return;
1240
1241         /* remove from hash chain */
1242         i = shash(df->path, Ndfhash);
1243         l = &dfhash[i];
1244         lk = &dfhlock[i];
1245         qlock(lk);
1246         lock(df);
1247         df->use--;
1248         if(df->old == 0 || df->use > 0){
1249                 unlock(df);
1250                 qunlock(lk);
1251                 return;
1252         }
1253         for(d = *l; d; d = *l){
1254                 if(d == df){
1255                         *l = d->next;
1256                         break;
1257                 }
1258                 l = &d->next;
1259         }
1260         unlock(df);
1261         qunlock(lk);
1262
1263         /* now we know it is unreferenced, remove it */
1264         freedf(df);
1265 }
1266
1267 /*
1268  *  search a dependency file for a symbol
1269  */
1270 Symbol*
1271 dfsearch(Dfile *df, char *name)
1272 {
1273         Symbol *dp;
1274
1275         if(df == nil)
1276                 return nil;
1277         for(dp = df->dhash[shash(name, df->hlen)]; dp; dp = dp->next)
1278                 if(strcmp(dp->sym, name) == 0)
1279                         return dp;
1280         return nil;
1281 }
1282
1283 /*
1284  *  resolve a single file into a vector of referenced files and the sum of their
1285  *  lengths
1286  */
1287 /* set a bit in the referenced file vector */
1288 int
1289 set(uchar *vec, int fno)
1290 {
1291         if(vec[fno/8] & (1<<(fno&7)))
1292                 return 1;
1293         vec[fno/8] |= 1<<(fno&7);
1294         return 0;
1295 }
1296 /* merge two referenced file vectors */
1297 void
1298 merge(uchar *vec, uchar *ovec, int nfile)
1299 {
1300         nfile = (nfile+7)/8;
1301         while(nfile-- > 0)
1302                 *vec++ |= *ovec++;
1303 }
1304 uint
1305 res(Dfile *df, uchar *vec, int fno)
1306 {
1307         File *f;
1308         Symbol *rp, *dp;
1309         int len;
1310
1311         f = &df->file[fno];
1312         if(set(vec, fno))
1313                 return 0;                               /* already set */
1314         if(f->refvec != nil){
1315                 merge(vec, f->refvec, df->nfile);       /* we've descended here before */
1316                 return f->tarlen;
1317         }
1318
1319         len = 0;
1320         for(rp = f->ref; rp; rp = rp->next){
1321                 dp = dfsearch(df, rp->sym);
1322                 if(dp == nil)
1323                         continue;
1324                 len += res(df, vec, dp->fno);
1325         }
1326         return len + Tblocksize + ((f->len + Tblocksize - 1)/Tblocksize)*Tblocksize;
1327 }
1328 void
1329 dfresolve(Dfile *df, int fno)
1330 {
1331         uchar *vec;
1332         File *f;
1333
1334         f = &df->file[fno];
1335         vec = emalloc((df->nfile+7)/8);
1336         f->tarlen = res(df, vec, fno);
1337         f->refvec = vec;
1338 }
1339
1340 /*
1341  *  make the tar directory block for a file
1342  */
1343 uchar*
1344 mktardir(File *f)
1345 {
1346         uchar *ep;
1347         Tardir *tp;
1348         uint sum;
1349         uchar *p, *cp;
1350
1351         p = emalloc(Tblocksize);
1352         tp = (Tardir*)p;
1353
1354         strcpy(tp->name, f->name);
1355         sprint(tp->mode, "%6o ", f->mode & 0777);
1356         sprint(tp->uid, "%6o ", 0);
1357         sprint(tp->gid, "%6o ", 0);
1358         sprint(tp->size, "%11o ", f->len);
1359         sprint(tp->mtime, "%11o ", f->mtime);
1360
1361         /* calculate checksum */
1362         memset(tp->chksum, ' ', sizeof(tp->chksum));
1363         sum = 0;
1364         ep = p + Tblocksize;
1365         for (cp = p; cp < ep; cp++)
1366                 sum += *cp;
1367         sprint(tp->chksum, "%6o", sum);
1368
1369         return p;
1370 }
1371
1372 /*
1373  *  manage open files
1374  */
1375 int
1376 getfile(Dfile *df, File *f)
1377 {
1378         int n;
1379         char path[512], *name;
1380
1381         qlock(f);
1382         f->use++;
1383         if(f->fd < 0){
1384                 name = strrchr(df->path, '/') + 1;
1385                 n = snprint(path, sizeof path, "%.*s/%s", 
1386                         utfnlen(df->path, name-df->path), df->path,
1387                         f->name);
1388                 if(n >= sizeof path - UTFmax){
1389                         syslog(0, dependlog, "path name too long: %.20s.../%.20s...", df->path, f->name);
1390                         return -1;
1391                 }
1392                 f->fd = open(path, OREAD);
1393                 if(f->fd < 0)
1394                         syslog(0, dependlog, "can't open %s: %r", path);
1395         }
1396
1397         return f->fd;
1398 }
1399 void
1400 releasefile(File *f)
1401 {
1402         --f->use;
1403         qunlock(f);
1404 }
1405 void
1406 closefile(File *f)
1407 {
1408         qlock(f);
1409         if(f->use == 0){
1410                 close(f->fd);
1411                 f->fd = -1;
1412         }
1413         qunlock(f);
1414 }
1415
1416 /*
1417  *  return a block of a tar file
1418  */
1419 int
1420 mktar(Dfile *df, Symbol *dp, uchar *area, uint offset, int len)
1421 {
1422         int fd, i, j, n, off;
1423         uchar *p, *buf;
1424         uchar *vec;
1425         File *f;
1426
1427         f = &df->file[dp->fno];
1428         vec = f->refvec;
1429         p = area;
1430
1431         /* find file */
1432         for(i = 0; i < df->nfile && len > 0; i++){
1433                 if((vec[i/8] & (1<<(i&7))) == 0)
1434                         continue;
1435
1436                 f = &df->file[i];
1437                 n = Tblocksize + ((f->len + Tblocksize - 1)/Tblocksize)*Tblocksize;
1438                 if(offset >= n){
1439                         offset -= n;
1440                         continue;
1441                 }
1442
1443                 if(offset < Tblocksize){
1444                         buf = mktardir(f);
1445                         if(offset + len > Tblocksize)
1446                                 j = Tblocksize - offset;
1447                         else
1448                                 j = len;
1449 //if(debug)fprint(2, "reading %d bytes dir of %s\n", j, f->name);
1450                         memmove(p, buf+offset, j);
1451                         p += j;
1452                         len -= j;
1453                         offset += j;
1454                         free(buf);
1455                 }
1456                 if(len <= 0)
1457                         break;
1458                 off = offset - Tblocksize;
1459                 if(off >= 0 && off < f->len){
1460                         if(off + len > f->len)
1461                                 j = f->len - off;
1462                         else
1463                                 j = len;
1464                         fd = getfile(df, f);
1465                         if(fd >= 0){
1466 //if(debug)fprint(2, "reading %d bytes from offset %d of %s\n", j, off, f->name);
1467                                 if(pread(fd, p, j, off) != j)
1468                                         syslog(0, dependlog, "%r reading %d bytes from offset %d of %s", j, off, f->name);
1469                         }
1470                         releasefile(f);
1471                         p += j;
1472                         len -= j;
1473                         offset += j;
1474                 }
1475                 if(len <= 0)
1476                         break;
1477                 if(offset < n){
1478                         if(offset + len > n)
1479                                 j = n - offset;
1480                         else
1481                                 j = len;
1482 //if(debug)fprint(2, "filling %d bytes after %s\n", j, f->name);
1483                         memset(p, 0, j);
1484                         p += j;
1485                         len -= j;
1486                 }
1487                 offset = 0;
1488         }
1489
1490         /* null blocks at end of tar file */
1491         if(offset < 2*Tblocksize && len > 0){
1492                 if(offset + len > 2*Tblocksize)
1493                         j = 2*Tblocksize - offset;
1494                 else
1495                         j = len;
1496 //if(debug)fprint(2, "filling %d bytes at end\n", j);
1497                 memset(p, 0, j);
1498                 p += j;
1499         }
1500
1501         return p - area;
1502 }
1503
1504 /*
1505  *  close the files making up  a tar file
1506  */
1507 void
1508 closetar(Dfile *df, Symbol *dp)
1509 {
1510         int i;
1511         uchar *vec;
1512         File *f;
1513
1514         f = &df->file[dp->fno];
1515         vec = f->refvec;
1516
1517         /* find file */
1518         for(i = 0; i < df->nfile; i++){
1519                 if((vec[i/8] & (1<<(i&7))) == 0)
1520                         continue;
1521                 closefile(&df->file[i]);
1522         }
1523 }
1524