]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/aux/depend.c
abaco: cleanup, handle image/x-icon, don't use backspace as a hotkey, and remove...
[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) == 0)
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                 while((n = read9pmsg(fs->fd, r->buf, messagesize)) == 0)
369                         ;
370                 qunlock(&iolock);
371                 if(n < 0)
372                         fatal("read9pmsg error: %r");
373
374                 if(convM2S(r->buf, n, &r->f) == 0){
375                         fprint(2, "can't convert %ux %ux %ux\n", r->buf[0],
376                                 r->buf[1], r->buf[2]);
377                         free(r);
378                         continue;
379                 }
380
381                 f = fsgetfid(fs, r->f.fid);
382                 r->fid = f;
383                 if(debug)
384                         fprint(2, "%F path %llux\n", &r->f, f->qid.path);
385
386                 t = r->f.type;
387                 r->f.type++;
388                 (*fcall[t])(fs, r, f);
389                 fsputfid(fs, f);
390         }
391
392 }
393
394 /*
395  *  any request that can get queued for a delayed reply
396  */
397 Request*
398 allocreq(int bufsize)
399 {
400         Request *r;
401
402         r = emalloc(sizeof(Request)+bufsize);
403         r->next = nil;
404         return r;
405 }
406
407 Fid*
408 fsgetfid(Fs *fs, int fid)
409 {
410         Fid *f, *nf;
411
412         lock(fs);
413         for(f = fs->hash[fid%Nfidhash]; f; f = f->next){
414                 if(f->fid == fid){
415                         f->ref++;
416                         unlock(fs);
417                         return f;
418                 }
419         }
420
421         nf = emalloc(sizeof(Fid));
422         nf->next = fs->hash[fid%Nfidhash];
423         fs->hash[fid%Nfidhash] = nf;
424         nf->fid = fid;
425         nf->ref = 1;
426         nf->fd = -1;
427         unlock(fs);
428         return nf;
429 }
430
431 void
432 fsputfid(Fs *fs, Fid *f)
433 {
434         Fid **l, *nf;
435
436         lock(fs);
437         if(--f->ref > 0){
438                 unlock(fs);
439                 return;
440         }
441         for(l = &fs->hash[f->fid%Nfidhash]; nf = *l; l = &nf->next)
442                 if(nf == f){
443                         *l = f->next;
444                         break;
445                 }
446         unlock(fs);
447         free(f);
448 }
449
450 void
451 fsreply(Fs *fs, Request *r, char *err)
452 {
453         int n;
454         uchar buf[MAXSIZE];
455
456         if(err){
457                 r->f.type = Rerror;
458                 r->f.ename = err;
459         }
460         if(debug)
461                 fprint(2, "%F path %llux\n", &r->f, r->fid->qid.path);
462         n = convS2M(&r->f, buf, messagesize);
463         if(n == 0)
464                 fatal("bad convS2M");
465         if(write(fs->fd, buf, n) != n)
466                 fatal("unmounted");
467         free(r);
468 }
469
470 void
471 fsversion(Fs *fs, Request *r, Fid*)
472 {
473         if(r->f.msize < 256){
474                 fsreply(fs, r, "version: bad message size");
475                 return;
476         }
477         if(messagesize > r->f.msize)
478                 messagesize = r->f.msize;
479         r->f.msize = messagesize;
480         r->f.version = "9P2000";
481         fsreply(fs, r, nil);
482 }
483
484 void
485 fsauth(Fs *fs, Request *r, Fid*)
486 {
487         fsreply(fs, r, "depend: authentication not required");
488 }
489
490 void
491 fsflush(Fs*, Request*, Fid*)
492 {
493 }
494
495 void
496 fsattach(Fs *fs, Request *r, Fid *f)
497 {
498         f->qid = fs->rootqid;
499         f->path = strdup("/");
500         f->df = getdf(mkpath(f->path, ".depend"));
501
502         /* hold down the fid till the clunk */
503         f->attached = 1;
504         lock(fs);
505         f->ref++;
506         unlock(fs);
507
508         r->f.qid = f->qid;
509         fsreply(fs, r, nil);
510 }
511
512 void
513 fswalk(Fs *fs, Request *r, Fid *f)
514 {
515         Fid *nf;
516         char *name, *tmp;
517         int i, nqid, nwname;
518         char errbuf[ERRMAX], *err;
519         Qid qid[MAXWELEM];
520         Dfile *lastdf;
521         char *path, *npath;
522         Dir *d;
523         Symbol *dp;
524
525         if(f->attached == 0){
526                 fsreply(fs, r, Eexist);
527                 return;
528         }
529
530         if(f->fd >= 0 || f->open)
531                 fatal("walk of an open file");
532
533         nf = nil;
534         if(r->f.newfid != r->f.fid){
535                 nf = fsgetfid(fs, r->f.newfid);
536                 nf->attached = 1;
537                 nf->open = f->open;
538                 nf->path = strdup(f->path);
539                 nf->qid = f->qid;
540                 nf->dp = f->dp;
541                 nf->fd = f->fd;
542                 nf->df = f->df;
543                 if(nf->df){
544                         lock(nf->df);
545                         nf->df->use++;
546                         unlock(nf->df);
547                 }
548                 if(r->f.nwname == 0){
549                         r->f.nwqid = 0;
550                         fsreply(fs, r, nil);
551                         return;
552                 }
553                 f = nf;
554         }
555
556         err = nil;
557         path = strdup(f->path);
558         if(path == nil)
559                 fatal(mallocerr);
560         nqid = 0;
561         nwname = r->f.nwname;
562         lastdf = f->df;
563
564         if(nwname > 0){
565                 for(; nqid<nwname; nqid++){
566                         name = r->f.wname[nqid];
567
568                         if(strcmp(name, ".") == 0){
569         Noop:
570                                 if(nqid == 0)
571                                         qid[nqid] = f->qid;
572                                 else
573                                         qid[nqid] = qid[nqid-1];
574                                 continue;
575                         }
576
577                         if(strcmp(name, "..") == 0){
578                                 name = strrchr(path, '/');
579                                 if(name){
580                                         if(name == path)        /* at root */
581                                                 goto Noop;
582                                         *name = '\0';
583                                 }
584                                 d = dirstat(path);
585                                 if(d == nil){
586                                         *name = '/';
587                                         errstr(errbuf, sizeof errbuf);
588                                         err = errbuf;
589                                         break;
590                                 }
591         Directory:
592                                 qid[nqid] = d->qid;
593                                 free(d);
594                                 releasedf(lastdf);
595                                 lastdf = getdf(mkpath(path, ".depend"));
596                                 continue;
597                         }
598
599                         npath = mkpath(path, name);
600                         free(path);
601                         path = npath;
602                         d = dirstat(path);
603
604                         if(d !=nil && (d->qid.type & QTDIR))
605                                 goto Directory;
606                         free(d);
607
608                         qid[nqid].type = QTFILE;
609                         qid[nqid].path = 0;
610                         qid[nqid].vers = 0;
611
612                         dp = dfsearch(lastdf, name);
613                         if(dp == nil){
614                                 tmp = strdup(name);
615                                 if(tmp == nil)
616                                         fatal("mallocerr");
617                                 i = strlen(tmp);
618                                 if(i > 4 && strcmp(&tmp[i-4], ".tar") == 0){
619                                         tmp[i-4] = 0;
620                                         dp = dfsearch(lastdf, tmp);
621                                 }
622                                 free(tmp);
623                         }
624
625                         if(dp == nil){
626                                 err = Eexist;
627                                 break;
628                         }
629                         qid[nqid].path = (uvlong)dp;
630                         qid[nqid].vers = 0;
631                 }
632                 if(nqid == 0 && err == nil)
633                         err = "file does not exist";
634         }
635
636         /* for error or partial success, put the cloned fid back*/
637         if(nf!=nil && (err != nil || nqid < nwname)){
638                 releasedf(nf->df);
639                 nf->df = nil;
640                 fsputfid(fs, nf);
641         }
642
643         if(err == nil){
644                 /* return (possibly short) list of qids */
645                 for(i=0; i<nqid; i++)
646                         r->f.wqid[i] = qid[i];
647                 r->f.nwqid = nqid;
648
649                 /* for full success, advance f */
650                 if(nqid > 0 && nqid == nwname){
651                         free(f->path);
652                         f->path = path;
653                         path = nil;
654
655                         f->qid = qid[nqid-1];
656                         f->dp = (Symbol*)f->qid.path;
657
658                         if(f->df != lastdf){
659                                 releasedf(f->df);
660                                 f->df = lastdf;
661                                 lastdf = nil;
662                         }
663
664                 }
665         }
666
667         releasedf(lastdf);
668         free(path);
669
670         fsreply(fs, r, err);
671 }
672
673 #ifdef adf
674 void
675 fsclone(Fs *fs, Request *r, Fid *f)
676 {
677         Fid *nf;
678
679         if(f->attached == 0){
680                 fsreply(fs, r, Eexist);
681                 return;
682         }
683         nf = fsgetfid(fs, r->f.newfid);
684
685         nf->attached = 1;
686         nf->open = f->open;
687         nf->path = strdup(f->path);
688         nf->qid = f->qid;
689         nf->dp = f->dp;
690         nf->fd = f->fd;
691         nf->df = f->df;
692         if(nf->df){
693                 lock(nf->df);
694                 nf->df->use++;
695                 unlock(nf->df);
696         }
697         fsreply(fs, r, nil);
698 }
699
700 void
701 fswalk(Fs *fs, Request *r, Fid *f)
702 {
703         char *name;
704         int i;
705         Dir d;
706         char errbuf[ERRLEN];
707         char *path;
708         Symbol *dp;
709
710         if(f->attached == 0){
711                 fsreply(fs, r, Enofid);
712                 return;
713         }
714
715         if(f->fd >= 0 || f->open)
716                 fatal("walk of an open file");
717
718         name = r->f.name;
719         if(strcmp(name, ".") == 0){
720                 fsreply(fs, r, nil);
721                 return;
722         }
723         if(strcmp(name, "..") == 0){
724                 name = strrchr(f->path, '/');
725                 if(name){
726                         if(name == f->path){
727                                 fsreply(fs, r, nil);
728                                 return;
729                         }
730                         *name = 0;
731                 }
732                 if(dirstat(f->path, &d) < 0){
733                         *name = '/';
734                         errstr(errbuf);
735                         fsreply(fs, r, errbuf);
736                         return;
737                 }
738                 r->f.qid = f->qid = d.qid;
739
740                 releasedf(f->df);
741                 f->df = getdf(mkpath(f->path, ".depend"));
742
743                 fsreply(fs, r, nil);
744                 return;
745         }
746
747         path = mkpath(f->path, name);
748         if(dirstat(path, &d) < 0 || (d.qid.path & CHDIR) == 0){
749                 dp = dfsearch(f->df, name);
750                 if(dp == nil){
751                         i = strlen(name);
752                         if(i > 4 && strcmp(&name[i-4], ".tar") == 0){
753                                 name[i-4] = 0;
754                                 dp = dfsearch(f->df, name);
755                         }
756                 }
757                 if(dp == nil){
758                         fsreply(fs, r, Eexist);
759                         free(path);
760                         return;
761                 }
762                 f->dp = dp;
763                 d.qid.path = (uint)dp;
764                 d.qid.vers = 0;
765         }
766
767         free(f->path);
768         f->path = path;
769
770         if(d.qid.path & CHDIR){
771                 releasedf(f->df);
772                 f->df = getdf(mkpath(f->path, ".depend"));
773         }
774
775         r->f.qid = f->qid = d.qid;
776         fsreply(fs, r, nil);
777 }
778 #endif
779 void
780 fsopen(Fs *fs, Request *r, Fid *f)
781 {
782         int mode;
783         char errbuf[ERRMAX];
784         
785         if(f->attached == 0){
786                 fsreply(fs, r, Enofid);
787                 return;
788         }
789         if(f->open){
790                 fsreply(fs, r, Eisopen);
791                 return;
792         }
793
794         mode = r->f.mode & 3;
795         if(mode != OREAD){
796                 fsreply(fs, r, Eperm);
797                 return;
798         }
799
800         if(f->qid.type & QTDIR){
801                 f->fd = open(f->path, OREAD);
802                 if(f->fd < 0){
803                         errstr(errbuf, sizeof errbuf);
804                         fsreply(fs, r, errbuf);
805                         return;
806                 }
807         }
808
809         f->open = 1;
810         r->f.qid = f->qid;
811         fsreply(fs, r, nil);
812 }
813
814 void
815 fscreate(Fs *fs, Request *r, Fid*)
816 {
817         fsreply(fs, r, Eperm);
818 }
819
820 void
821 fsread(Fs *fs, Request *r, Fid *f)
822 {
823         int i, n, len,skip;
824         Dir d;
825         Symbol *dp;
826         char buf[512];
827
828         if(f->attached == 0){
829                 fsreply(fs, r, Enofid);
830                 return;
831         }
832         if((int)r->f.count < 0){
833                 fsreply(fs, r, "bad read count");
834                 return;
835         }
836
837         if(f->qid.type & QTDIR){
838                 n = 0;
839                 if(f->dir == nil){
840                         f->ndir = dirreadall(f->fd, &f->dir);
841                         f->dirindex = 0;
842                 }
843                 if(f->dir == nil)
844                         goto Return;
845                 if(r->f.offset == 0)    /* seeking to zero is permitted */
846                         f->dirindex = 0;
847                 for(; f->dirindex < f->ndir; f->dirindex++){
848                         if((f->dir[f->dirindex].qid.type & QTDIR) == 0)
849                                 continue;
850                         len = convD2M(&f->dir[f->dirindex], r->buf+n, r->f.count-n);
851                         if(len <= BIT16SZ)
852                                 goto Return;
853                         n += len;
854                 }
855
856                 skip = f->dirindex - f->ndir;   /* # depend records already read */
857
858                 if(f->df){
859                         for(i = 0; i < f->df->hlen; i++)
860                                 for(dp = f->df->dhash[i]; dp; dp = dp->next){
861                                         if(skip-- > 0)
862                                                 continue;
863                                         snprint(buf, sizeof buf, "%s.tar", dp->sym);
864                                         d.name = buf;
865                                         d.uid = "none";
866                                         d.gid = "none";
867                                         d.muid = "none";
868                                         d.qid.type = QTFILE;
869                                         d.qid.path = (uvlong)dp;
870                                         d.qid.vers = 0;
871                                         d.length = f->df->file[dp->fno].tarlen;
872                                         d.mode = 0444;
873                                         d.mtime = time(nil);
874                                         d.atime = time(nil);
875                                         len = convD2M(&d, r->buf + n, r->f.count - n);
876                                         if(len <= BIT16SZ)
877                                                 goto Return;
878                                         n += len;
879                                         f->dirindex++;
880                                 }
881                 }
882         } else
883                 n = mktar(f->df, f->dp, r->buf, r->f.offset, r->f.count);
884
885     Return:
886         r->f.data = (char*)r->buf;
887         r->f.count = n;
888         fsreply(fs, r, nil);
889 }
890
891 void
892 fswrite(Fs *fs, Request *r, Fid*)
893 {
894         fsreply(fs, r, Eperm);
895 }
896
897 void
898 fsclunk(Fs *fs, Request *r, Fid *f)
899 {
900         if(f->attached == 0){
901                 fsreply(fs, r, Enofid);
902                 return;
903         }
904         if(f->fd >= 0){
905                 close(f->fd);
906                 f->fd = -1;
907         }
908
909         if((f->qid.type & QTDIR) == 0)
910                 closetar(f->df, f->dp);
911
912         releasedf(f->df);
913         f->df = nil;
914         free(f->dir);
915         f->dir = nil;
916
917         fsreply(fs, r, nil);
918         fsputfid(fs, f);
919 }
920
921 void
922 fsremove(Fs *fs, Request *r, Fid*)
923 {
924         fsreply(fs, r, Eperm);
925 }
926
927 void
928 fsstat(Fs *fs, Request *r, Fid *f)
929 {
930         char err[ERRMAX];
931         Dir d;
932         Symbol *dp;
933         int n;
934         uchar statbuf[Nstat];
935
936         if(f->qid.type & QTDIR)
937                 n = stat(f->path, statbuf, sizeof statbuf);
938         else {
939                 dp = f->dp;
940                 d.name = dp->sym;
941                 d.uid = "none";
942                 d.gid = "none";
943                 d.muid = "none";
944                 d.qid.type = QTFILE;
945                 d.qid.path = (uvlong)dp;
946                 d.qid.vers = 0;
947                 d.length = f->df->file[dp->fno].tarlen;
948                 d.mode = 0444;
949                 d.mtime = time(nil);
950                 d.atime = time(nil);
951                 n = convD2M(&d, statbuf, sizeof statbuf);
952         }
953         if(n <= BIT16SZ){
954                 errstr(err, sizeof err);
955                 fsreply(fs, r, err);
956         } else {
957                 r->f.stat = statbuf;
958                 r->f.nstat = n;
959                 fsreply(fs, r, nil);
960         }
961 }
962
963 void
964 fswstat(Fs *fs, Request *r, Fid*)
965 {
966         fsreply(fs, r, Eperm);
967 }
968
969 /*
970  *  string hash
971  */
972 uint
973 shash(char *str, int len)
974 {
975         uint    hash;
976         char    *val; 
977
978         hash = 0;
979         for(val = str; *val; val++)
980                 hash = (hash*13) + *val-'a';
981         return hash % len;
982 }
983
984 /*
985  *  free info about a dependency file
986  */
987 void
988 freedf(Dfile *df)
989 {
990         int i;
991         Symbol *dp, *next;
992
993         lock(df);
994         df->old = 1;
995         if(df->use){
996                 unlock(df);
997                 return;
998         }
999
1000         unlock(df);     /* we're no longer referenced */
1001         for(i = 0; i < df->nfile; i++)
1002                 free(df->file[i].name);
1003         free(df->file[i].refvec);
1004         free(df->file);
1005         df->file = nil;
1006
1007         for(i = 0; i < df->hlen; i++)
1008                 for(dp = df->dhash[i]; dp != nil; dp = next){
1009                         next = dp->next;
1010                         free(dp);
1011                 }
1012
1013         free(df->path);
1014         free(df);
1015         free(df->dhash);
1016         df->dhash = nil;
1017 }
1018
1019 /*
1020  *  crack a dependency file
1021  */
1022 void
1023 newsym(char *name, int fno, Symbol **l)
1024 {
1025         Symbol *dp;
1026
1027         dp = emalloc(sizeof(Symbol));
1028         dp->sym = strdup(name);
1029         if(dp->sym == nil)
1030                 fatal("mallocerr");
1031         dp->next = *l;
1032         dp->fno = fno;
1033         *l = dp;
1034 }
1035 int
1036 awk(Biobuf *b, char **field, int n)
1037 {
1038         char *line;
1039         int i;
1040
1041         while(line = Brdline(b, '\n')){
1042                 line[Blinelen(b)-1] = 0;
1043                 while(*line == ' ' || *line == '\t')
1044                         *line++ = 0;
1045                 for(i = 0; i < n; i++){
1046                         if(*line == 0 || *line == '#')
1047                                 break;
1048                         field[i] = line;
1049                         while(*line && *line != ' ' && *line != '\t')
1050                                 line++;
1051                         while(*line == ' ' || *line == '\t')
1052                                 *line++ = 0;
1053                 }
1054                 if(i)
1055                         return i;
1056         }
1057
1058         return 0;
1059 }
1060
1061 void
1062 crackdf(Dfile *df, Biobuf *b, uint len, char *dpath)
1063 {
1064         char *name;
1065         char *field[3];
1066         char path[512];
1067         int n, inc, ok, npath;
1068         Symbol **l, *dp, *next;
1069         File *f, *ef;
1070         Dir *d;
1071
1072         inc = 32;
1073         df->flen = inc;
1074         df->file = emalloc(df->flen*sizeof(File));
1075         df->nfile = 0;
1076
1077         df->hlen = 1 + len/8;
1078         df->dhash = emalloc(df->hlen*sizeof(Symbol*));
1079
1080         l = nil;
1081         while((n = awk(b, field, 3)) > 0){
1082                 if(n != 2)
1083                         continue;
1084
1085                 name = field[1];
1086                 switch(*field[0]){
1087                 case 'F':
1088                         if(df->flen == df->nfile){
1089                                 df->flen += inc;
1090                                 df->file = realloc(df->file, df->flen*sizeof(File));
1091                                 if(df->file == nil)
1092                                         fatal(mallocerr);
1093                                 memset(&df->file[df->nfile], 0, inc*sizeof(File));
1094                         }
1095                         f = &df->file[df->nfile++];
1096                         f->name = strdup(name);
1097                         l = &f->ref;
1098                         /* fall through and define as a symbol */
1099                 case 'D':
1100                         if(l == nil)
1101                                 continue;
1102                         newsym(name, df->nfile-1, &(df->dhash[shash(name, df->hlen)]));
1103                         break;
1104                 case 'R':
1105                         if(l == nil)
1106                                 continue;
1107                         newsym(name, 0, l);
1108                         break;
1109                 }
1110         }
1111
1112         ef = &df->file[df->nfile];
1113
1114         /* stat the files to get sizes */
1115         npath = strlen(dpath);
1116         if(npath+1+1 >= sizeof path)
1117                 fatal(Etoolong);
1118         memmove(path, dpath, npath+1);  /* include NUL */
1119         name = strrchr(path, '/') + 1;
1120         for(f = df->file; f < ef; f++){
1121                 n = strlen(f->name);
1122                 if(npath+1+n+3+1 > sizeof path)
1123                         fatal(Etoolong);
1124                 memmove(name, f->name, n+1);    /* include NUL */
1125                 ok = access(path, AEXIST);
1126                 if(ok < 0){
1127                         strcpy(name+n, ".Z");
1128                         ok = access(path, AEXIST);
1129                         if(ok < 0){
1130                                 strcpy(name+n, ".gz");
1131                                 ok = access(path, AEXIST);
1132                         }
1133                 }
1134                 if(ok >= 0){
1135                         free(f->name);
1136                         f->name = strdup(name);
1137                         if(f->name == nil)
1138                                 fatal(mallocerr);
1139                 }
1140                 d = dirstat(path);
1141                 if(d){
1142                         f->len = d->length;
1143                         f->mode = d->mode;
1144                         f->mtime = d->mtime;
1145                         free(d);
1146                 }else{
1147                         f->len = 0;
1148                         f->mode = 0;
1149                         f->mtime = 0;
1150                 }
1151                 f->fd = -1;
1152         }
1153
1154         /* resolve all file references */
1155         for(f = df->file; f < ef; f++)
1156                 dfresolve(df, f-df->file);
1157
1158         /* free the referenced symbols, don't need them anymore */
1159         for(f = df->file; f < ef; f++){
1160                 f->tarlen += 2*Tblocksize;      /* tars trailing 0 blocks */
1161                 for(dp = f->ref; dp != nil; dp = next){
1162                         next = dp->next;
1163                         free(dp);
1164                 }
1165                 f->ref = nil;
1166         }
1167 }
1168
1169 /*
1170  *  get a cracked dependency file
1171  */
1172 Dfile*
1173 getdf(char *path)
1174 {
1175         Dfile *df, **l;
1176         QLock *lk;
1177         Dir *d;
1178         int i, fd;
1179         Biobuf *b;
1180
1181         i = shash(path, Ndfhash);
1182         l = &dfhash[i];
1183         lk = &dfhlock[i];
1184         qlock(lk);
1185         for(df = *l; df; df = *l){
1186                 if(strcmp(path, df->path) == 0)
1187                         break;
1188                 l = &df->next;
1189         }
1190         d = dirstat(path);
1191
1192         if(df){
1193                 if(d!=nil && d->qid.type == df->qid.type && d->qid.vers == df->qid.vers && d->qid.vers == df->qid.vers){
1194                         free(path);
1195                         lock(df);
1196                         df->use++;
1197                         unlock(df);
1198                         goto Return;
1199                 }
1200                 *l = df->next;
1201                 freedf(df);
1202         }
1203
1204         fd = open(path, OREAD);
1205         if(d == nil || fd < 0){
1206                 close(fd);
1207                 goto Return;
1208         }
1209
1210         df = emalloc(sizeof(*df));
1211         b = emalloc(sizeof(Biobuf));
1212
1213         Binit(b, fd, OREAD);
1214         df->qid = d->qid;
1215         df->path = path;
1216         crackdf(df, b, d->length, path);
1217         Bterm(b);
1218
1219         free(b);
1220
1221         df->next = *l;
1222         *l = df;
1223         df->use = 1;
1224     Return:
1225         qunlock(lk);
1226         free(d);
1227         return df;
1228 }
1229
1230 /*
1231  *  stop using a dependency file.  Free it if it is no longer linked in.
1232  */
1233 void
1234 releasedf(Dfile *df)
1235 {
1236         Dfile **l, *d;
1237         QLock *lk;
1238         int i;
1239
1240         if(df == nil)
1241                 return;
1242
1243         /* remove from hash chain */
1244         i = shash(df->path, Ndfhash);
1245         l = &dfhash[i];
1246         lk = &dfhlock[i];
1247         qlock(lk);
1248         lock(df);
1249         df->use--;
1250         if(df->old == 0 || df->use > 0){
1251                 unlock(df);
1252                 qunlock(lk);
1253                 return;
1254         }
1255         for(d = *l; d; d = *l){
1256                 if(d == df){
1257                         *l = d->next;
1258                         break;
1259                 }
1260                 l = &d->next;
1261         }
1262         unlock(df);
1263         qunlock(lk);
1264
1265         /* now we know it is unreferenced, remove it */
1266         freedf(df);
1267 }
1268
1269 /*
1270  *  search a dependency file for a symbol
1271  */
1272 Symbol*
1273 dfsearch(Dfile *df, char *name)
1274 {
1275         Symbol *dp;
1276
1277         if(df == nil)
1278                 return nil;
1279         for(dp = df->dhash[shash(name, df->hlen)]; dp; dp = dp->next)
1280                 if(strcmp(dp->sym, name) == 0)
1281                         return dp;
1282         return nil;
1283 }
1284
1285 /*
1286  *  resolve a single file into a vector of referenced files and the sum of their
1287  *  lengths
1288  */
1289 /* set a bit in the referenced file vector */
1290 int
1291 set(uchar *vec, int fno)
1292 {
1293         if(vec[fno/8] & (1<<(fno&7)))
1294                 return 1;
1295         vec[fno/8] |= 1<<(fno&7);
1296         return 0;
1297 }
1298 /* merge two referenced file vectors */
1299 void
1300 merge(uchar *vec, uchar *ovec, int nfile)
1301 {
1302         nfile = (nfile+7)/8;
1303         while(nfile-- > 0)
1304                 *vec++ |= *ovec++;
1305 }
1306 uint
1307 res(Dfile *df, uchar *vec, int fno)
1308 {
1309         File *f;
1310         Symbol *rp, *dp;
1311         int len;
1312
1313         f = &df->file[fno];
1314         if(set(vec, fno))
1315                 return 0;                               /* already set */
1316         if(f->refvec != nil){
1317                 merge(vec, f->refvec, df->nfile);       /* we've descended here before */
1318                 return f->tarlen;
1319         }
1320
1321         len = 0;
1322         for(rp = f->ref; rp; rp = rp->next){
1323                 dp = dfsearch(df, rp->sym);
1324                 if(dp == nil)
1325                         continue;
1326                 len += res(df, vec, dp->fno);
1327         }
1328         return len + Tblocksize + ((f->len + Tblocksize - 1)/Tblocksize)*Tblocksize;
1329 }
1330 void
1331 dfresolve(Dfile *df, int fno)
1332 {
1333         uchar *vec;
1334         File *f;
1335
1336         f = &df->file[fno];
1337         vec = emalloc((df->nfile+7)/8);
1338         f->tarlen = res(df, vec, fno);
1339         f->refvec = vec;
1340 }
1341
1342 /*
1343  *  make the tar directory block for a file
1344  */
1345 uchar*
1346 mktardir(File *f)
1347 {
1348         uchar *ep;
1349         Tardir *tp;
1350         uint sum;
1351         uchar *p, *cp;
1352
1353         p = emalloc(Tblocksize);
1354         tp = (Tardir*)p;
1355
1356         strcpy(tp->name, f->name);
1357         sprint(tp->mode, "%6o ", f->mode & 0777);
1358         sprint(tp->uid, "%6o ", 0);
1359         sprint(tp->gid, "%6o ", 0);
1360         sprint(tp->size, "%11o ", f->len);
1361         sprint(tp->mtime, "%11o ", f->mtime);
1362
1363         /* calculate checksum */
1364         memset(tp->chksum, ' ', sizeof(tp->chksum));
1365         sum = 0;
1366         ep = p + Tblocksize;
1367         for (cp = p; cp < ep; cp++)
1368                 sum += *cp;
1369         sprint(tp->chksum, "%6o", sum);
1370
1371         return p;
1372 }
1373
1374 /*
1375  *  manage open files
1376  */
1377 int
1378 getfile(Dfile *df, File *f)
1379 {
1380         int n;
1381         char path[512], *name;
1382
1383         qlock(f);
1384         f->use++;
1385         if(f->fd < 0){
1386                 name = strrchr(df->path, '/') + 1;
1387                 n = snprint(path, sizeof path, "%.*s/%s", (int)(name-df->path), df->path, 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