Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
		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*
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*
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
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
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
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
484
fsauth(Fs *fs, Request *r, Fid*)
485
{
486
	fsreply(fs, r, "depend: authentication not required");
487
}
488
 
489
void
490
fsflush(Fs*, Request*, Fid*)
491
{
492
}
493
 
494
void
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
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
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
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
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
814
fscreate(Fs *fs, Request *r, Fid*)
815
{
816
	fsreply(fs, r, Eperm);
817
}
818
 
819
void
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
891
fswrite(Fs *fs, Request *r, Fid*)
892
{
893
	fsreply(fs, r, Eperm);
894
}
895
 
896
void
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
921
fsremove(Fs *fs, Request *r, Fid*)
922
{
923
	fsreply(fs, r, Eperm);
924
}
925
 
926
void
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
963
fswstat(Fs *fs, Request *r, Fid*)
964
{
965
	fsreply(fs, r, Eperm);
966
}
967
 
968
/*
969
 *  string hash
970
 */
971
uint
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
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
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
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
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*
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
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*
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
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
1299
merge(uchar *vec, uchar *ovec, int nfile)
1300
{
1301
	nfile = (nfile+7)/8;
1302
	while(nfile-- > 0)
1303
		*vec++ |= *ovec++;
1304
}
1305
uint
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
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*
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
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
1399
releasefile(File *f)
1400
{
1401
	--f->use;
1402
	qunlock(f);
1403
}
1404
void
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
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
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