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