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