xref: /plan9/sys/src/cmd/aux/depend.c (revision 22a127bbfe4dd304949cc596400de973c0138e31)
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