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 <bio.h>
6
#include <mp.h>
7
#include <libsec.h>
8
#include <flate.h>
9
 
10
#include "paqfs.h"
11
 
12
enum
13
{
14
	OPERM	= 0x3,		/* mask of all permission types in open mode */
15
	OffsetSize = 4,		/* size in bytes of an offset */
16
};
17
 
18
typedef struct Fid Fid;
19
typedef struct Paq Paq;
20
typedef struct Block Block;
21
 
22
struct Fid
23
{
24
	short	busy;
25
	short	open;
26
	int	fid;
27
	char	*user;
28
	ulong	offset;		/* for directory reading */
29
 
30
	Paq	*paq;
31
	Fid	*next;
32
};
33
 
34
struct Paq
35
{	
36
	int ref;
37
	Paq *up;
38
	PaqDir *dir;
39
	Qid qid;
40
};
41
 
42
struct Block
43
{
44
	int ref;
45
	ulong addr;	/* block byte address */
46
	ulong age;
47
	uchar *data;
48
};
49
 
50
enum
51
{
52
	Pexec =		1,
53
	Pwrite = 	2,
54
	Pread = 	4,
55
	Pother = 	1,
56
	Pgroup = 	8,
57
	Powner =	64,
58
};
59
 
60
int	noauth;
61
Fid	*fids;
62
Fcall	rhdr, thdr;
63
int 	blocksize;
64
int 	cachesize = 20;
65
int	mesgsize = 8*1024 + IOHDRSZ;
66
Paq 	*root, *rootfile;
67
Block 	*cache;
68
ulong 	cacheage;
69
Biobuf	*bin;
70
int	qflag;
71
 
72
Fid *	newfid(int);
73
void	paqstat(PaqDir*, char*);
74
void	io(int fd);
75
void	*erealloc(void*, ulong);
76
void	*emalloc(ulong);
77
void 	*emallocz(ulong n);
78
char 	*estrdup(char*);
79
void	usage(void);
80
ulong	getl(uchar *p);
81
int	gets(uchar *p);
82
char 	*getstr(uchar *p);
83
PaqDir	*getDir(uchar*);
84
void	getHeader(uchar *p, PaqHeader *b);
85
void	getBlock(uchar *p, PaqBlock *b);
86
void	getTrailer(uchar *p, PaqTrailer *b);
87
void	init(char*, int);
88
void	paqDirFree(PaqDir*);
89
Qid	paqDirQid(PaqDir *d);
90
Paq	*paqCpy(Paq *s);
91
Paq	*paqLookup(Paq *s, char *name);
92
void	paqFree(Paq*);
93
Paq	*paqWalk(Paq *s, char *name);
94
int	perm(PaqDir *s, char *user, int p);
95
int	dirRead(Fid*, uchar*, int);
96
Block	*blockLoad(ulong addr, int type);
97
void	blockFree(Block*);
98
int	checkDirSize(uchar *p, uchar *ep);
99
int	packDir(PaqDir*, uchar*, int);
100
int	blockRead(uchar *data, ulong addr, int type);
101
void	readHeader(PaqHeader *hdr, char *name, DigestState *ds);
102
void	readBlocks(char *name, DigestState *ds);
103
void	readTrailer(PaqTrailer *tlr, char *name, DigestState *ds);
104
 
105
char	*rflush(Fid*), *rversion(Fid*),
106
	*rauth(Fid*), *rattach(Fid*), *rwalk(Fid*),
107
	*ropen(Fid*), *rcreate(Fid*),
108
	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
109
	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);
110
 
111
char 	*(*fcalls[])(Fid*) = {
112
	[Tflush]	rflush,
113
	[Tversion]	rversion,
114
	[Tattach]	rattach,
115
	[Tauth]		rauth,
116
	[Twalk]		rwalk,
117
	[Topen]		ropen,
118
	[Tcreate]	rcreate,
119
	[Tread]		rread,
120
	[Twrite]	rwrite,
121
	[Tclunk]	rclunk,
122
	[Tremove]	rremove,
123
	[Tstat]		rstat,
124
	[Twstat]	rwstat,
125
};
126
 
127
char	Eperm[] =	"permission denied";
128
char	Enotdir[] =	"not a directory";
129
char	Enoauth[] =	"authentication not required";
130
char	Enotexist[] =	"file does not exist";
131
char	Einuse[] =	"file in use";
132
char	Eexist[] =	"file exists";
133
char	Enotowner[] =	"not owner";
134
char	Eisopen[] = 	"file already open for I/O";
135
char	Excl[] = 	"exclusive use file already open";
136
char	Ename[] = 	"illegal name";
137
char	Erdonly[] = 	"read only file system";
138
char	Ebadblock[] = 	"bad block";
139
char	Eversion[] = 	"bad version of P9";
140
char	Edirtoobig[] = 	"directory entry too big";
141
 
142
int debug;
143
 
144
#pragma varargck	type	"V"	uchar*
145
 
146
static int
147
sha1fmt(Fmt *f)
148
{
149
	int i;
150
	uchar *v;
151
 
152
	v = va_arg(f->args, uchar*);
153
	if(v == nil){
154
		fmtprint(f, "*");
155
	}
156
	else{
157
		for(i = 0; i < SHA1dlen; i++)
158
			fmtprint(f, "%2.2ux", v[i]);
159
	}
160
 
161
	return 0;
162
}
163
 
164
void
165
main(int argc, char *argv[])
166
{
167
	int pfd[2];
168
	int fd, mnt, srv, stdio, verify;
169
	char buf[64], *mntpoint, *srvname, *p;
170
 
171
	fmtinstall('V', sha1fmt);
172
 
173
	mntpoint = "/n/paq";
174
	srvname = "paqfs";
175
	mnt = 1;
176
	srv = stdio = verify = 0;
177
 
178
	ARGBEGIN{
179
	default:
180
		usage();
181
	case 'a':
182
		noauth = 1;
183
		break;
184
	case 'c':
185
		p = EARGF(usage());
186
		cachesize = atoi(p);
187
		break;
188
	case 'd':
189
		debug = 1;
190
		break;
191
	case 'i':
192
		mnt = 0;
193
		stdio = 1;
194
		pfd[0] = 0;
195
		pfd[1] = 1;
196
		break;
197
	case 'm':
198
		mntpoint = EARGF(usage());
199
		break;
200
	case 'M':
201
		p = EARGF(usage());
202
		mesgsize = atoi(p);
203
		if(mesgsize < 512)
204
			mesgsize = 512;
205
		if(mesgsize > 128*1024)
206
			mesgsize = 128*1024;
207
		break;
208
	case 'p':
209
		srv = 1;
210
		mnt = 1;
211
		break;
212
	case 'q':
213
		qflag = 1;
214
		break;
215
	case 's':
216
		srv = 1;
217
		mnt = 0;
218
		break;
219
	case 'S':
220
		srvname = EARGF(usage());
221
		break;
222
	case 'v':
223
		verify = 1;
224
		break;
225
	}ARGEND
226
 
227
	if(argc != 1)
228
		usage();
229
 
230
	init(argv[0], verify);
231
 
232
	if(!stdio){
233
		if(pipe(pfd) < 0)
234
			sysfatal("pipe: %r");
235
		if(srv){
236
			snprint(buf, sizeof buf, "#s/%s", srvname);
237
			fd = create(buf, OWRITE, 0666);
238
			if(fd < 0)
239
				sysfatal("create %s: %r", buf);
240
			if(fprint(fd, "%d", pfd[0]) < 0)
241
				sysfatal("write %s: %r", buf);
242
		}
243
	}
244
 
245
	if(debug)
246
		fmtinstall('F', fcallfmt);
247
	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){
248
	case -1:
249
		sysfatal("fork");
250
	case 0:
251
		close(pfd[0]);
252
		io(pfd[1]);
253
		break;
254
	default:
255
		close(pfd[1]);	/* don't deadlock if child fails */
256
		if(mnt && mount(pfd[0], -1, mntpoint, MREPL|MCREATE, "") < 0)
257
			sysfatal("mount %s: %r", mntpoint);
258
	}
259
	exits(0);
260
}
261
 
262
char*
263
rversion(Fid*)
264
{
265
	Fid *f;
266
 
267
	for(f = fids; f; f = f->next)
268
		if(f->busy)
269
			rclunk(f);
270
	if(rhdr.msize > mesgsize)
271
		thdr.msize = mesgsize;
272
	else
273
		thdr.msize = rhdr.msize;
274
	if(strcmp(rhdr.version, "9P2000") != 0)
275
		return Eversion;
276
	thdr.version = "9P2000";
277
	return 0;
278
}
279
 
280
char*
281
rauth(Fid*)
282
{
283
	return Enoauth;
284
}
285
 
286
char*
287
rflush(Fid *f)
288
{
289
	USED(f);
290
	return 0;
291
}
292
 
293
char*
294
rattach(Fid *f)
295
{
296
	/* no authentication! */
297
	f->busy = 1;
298
	f->paq = paqCpy(root);
299
	thdr.qid = f->paq->qid;
300
	if(rhdr.uname[0])
301
		f->user = estrdup(rhdr.uname);
302
	else
303
		f->user = "none";
304
	return 0;
305
}
306
 
307
char*
308
clone(Fid *f, Fid **res)
309
{
310
	Fid *nf;
311
 
312
	if(f->open)
313
		return Eisopen;
314
	if(f->busy == 0)
315
		return Enotexist;
316
	nf = newfid(rhdr.newfid);
317
	nf->busy = 1;
318
	nf->open = 0;
319
	nf->paq = paqCpy(f->paq);
320
	nf->user = strdup(f->user);
321
	*res = nf;
322
	return 0;
323
}
324
 
325
char*
326
rwalk(Fid *f)
327
{
328
	Paq *paq, *npaq;
329
	Fid *nf;
330
	int nqid, nwname;
331
	Qid qid;
332
	char *err;
333
 
334
	if(f->busy == 0)
335
		return Enotexist;
336
	nf = nil;
337
	if(rhdr.fid != rhdr.newfid){
338
		err = clone(f, &nf);
339
		if(err)
340
			return err;
341
		f = nf;	/* walk the new fid */
342
	}
343
 
344
	nwname = rhdr.nwname;
345
 
346
	/* easy case */
347
	if(nwname == 0) {
348
		thdr.nwqid = 0;
349
		return 0;
350
	}
351
 
352
	paq = paqCpy(f->paq);
353
	qid = paq->qid;
354
	err = nil;
355
 
356
	for(nqid = 0; nqid < nwname; nqid++){
357
		if((qid.type & QTDIR) == 0){
358
			err = Enotdir;
359
			break;
360
		}
361
		if(!perm(paq->dir, f->user, Pexec)) {
362
			err = Eperm;
363
			break;
364
		}
365
		npaq = paqWalk(paq, rhdr.wname[nqid]);
366
		if(npaq == nil) {
367
			err = Enotexist;
368
			break;
369
		}
370
		paqFree(paq);
371
		paq = npaq;
372
		qid = paq->qid;
373
		thdr.wqid[nqid] = qid;
374
	}
375
 
376
	thdr.nwqid = nqid;
377
 
378
	if(nqid == nwname){
379
		/* success */
380
		paqFree(f->paq);
381
		f->paq = paq;
382
		return 0;
383
	}
384
 
385
	paqFree(paq);
386
	if(nf != nil)
387
		rclunk(nf);
388
 
389
	/* only error on the first element */
390
	if(nqid == 0)
391
		return err;
392
 
393
	return 0;
394
}
395
 
396
char *
397
ropen(Fid *f)
398
{
399
	int mode, trunc;
400
 
401
	if(f->open)
402
		return Eisopen;
403
	if(f->busy == 0)
404
		return Enotexist;
405
	mode = rhdr.mode;
406
	if(f->paq->qid.type & QTDIR){
407
		if(mode != OREAD)
408
			return Eperm;
409
		thdr.qid = f->paq->qid;
410
		return 0;
411
	}
412
	if(mode & ORCLOSE)
413
		return Erdonly;
414
	trunc = mode & OTRUNC;
415
	mode &= OPERM;
416
	if(mode==OWRITE || mode==ORDWR || trunc)
417
		return Erdonly;
418
	if(mode==OREAD)
419
		if(!perm(f->paq->dir, f->user, Pread))
420
			return Eperm;
421
	if(mode==OEXEC)
422
		if(!perm(f->paq->dir, f->user, Pexec))
423
			return Eperm;
424
	thdr.qid = f->paq->qid;
425
	f->open = 1;
426
	return 0;
427
}
428
 
429
char *
430
rcreate(Fid *f)
431
{
432
	if(f->open)
433
		return Eisopen;
434
	if(f->busy == 0)
435
		return Enotexist;
436
	return Erdonly;
437
}
438
 
439
char *
440
readdir(Fid *f)
441
{
442
	PaqDir *pd;
443
	uchar *p, *ep;
444
	ulong off;
445
	int n, cnt, i;
446
	uchar *buf;
447
	Block *ptr, *b;
448
 
449
	buf = (uchar*)thdr.data;
450
	cnt = rhdr.count;
451
	if(rhdr.offset == 0)
452
		f->offset = 0;
453
	off = f->offset;
454
 
455
	if(rootfile && f->paq == root){
456
		if(off != 0){
457
			rhdr.count = 0;
458
			return nil;
459
		}
460
		n = packDir(rootfile->dir, buf, cnt);
461
		rhdr.count = n;
462
		return nil;
463
	}
464
 
465
	ptr = blockLoad(f->paq->dir->offset, PointerBlock);
466
	if(ptr == nil)
467
		return Ebadblock;
468
	i = off/blocksize;
469
	off -= i*blocksize;
470
 
471
	thdr.count = 0;
472
	b = blockLoad(getl(ptr->data + i*4), DirBlock);
473
	while(b != nil) {
474
		p = b->data + off;
475
		ep = b->data + blocksize;
476
		if(checkDirSize(p, ep)) {
477
			pd = getDir(p);
478
			n = packDir(pd, buf, cnt);
479
			paqDirFree(pd);
480
			if(n == 0) {
481
				blockFree(b);
482
				if(thdr.count == 0) {
483
					blockFree(ptr);
484
					return Edirtoobig;
485
				}
486
				break;
487
			}
488
			off += gets(p);
489
			cnt -= n;
490
			buf += n;
491
			thdr.count += n;
492
		} else {
493
			off = 0;
494
			i++;
495
			blockFree(b);
496
			b = blockLoad(getl(ptr->data + i*4), DirBlock);
497
		}
498
	}
499
	f->offset = i*blocksize + off;
500
	blockFree(ptr);
501
 
502
	return 0;
503
}
504
 
505
char*
506
rread(Fid *f)
507
{
508
	PaqDir *pd;
509
	uchar *buf;
510
	vlong off;
511
	ulong uoff;
512
	int n, cnt, i;
513
	Block *ptr, *b;
514
 
515
	if(f->busy == 0)
516
		return Enotexist;
517
	if(f->paq->qid.type & QTDIR)
518
		return readdir(f);
519
	pd = f->paq->dir;
520
	off = rhdr.offset;
521
	buf = (uchar*)thdr.data;
522
	cnt = rhdr.count;
523
 
524
	thdr.count = 0;
525
	if(off >= pd->length || cnt == 0)
526
		return 0;
527
 
528
	if(cnt > pd->length - off)
529
		cnt = pd->length - off;
530
 
531
	ptr = blockLoad(pd->offset, PointerBlock);
532
	if(ptr == nil)
533
		return Ebadblock;
534
 
535
	i = off/blocksize;
536
	uoff = off-i*blocksize;
537
 
538
	while(cnt > 0) {
539
		b = blockLoad(getl(ptr->data + i*4), DataBlock);
540
		if(b == nil) {
541
			blockFree(ptr);
542
			return Ebadblock;
543
		}
544
		n = blocksize - uoff;
545
		if(n > cnt)
546
			n = cnt;
547
		memmove(buf, b->data + uoff, n);
548
		cnt -= n;
549
		thdr.count += n;
550
		buf += n;
551
		uoff = 0;
552
		i++;
553
		blockFree(b);
554
	}
555
	blockFree(ptr);
556
	return 0;
557
}
558
 
559
char*
560
rwrite(Fid *f)
561
{
562
	if(f->busy == 0)
563
		return Enotexist;
564
	return Erdonly;
565
}
566
 
567
char *
568
rclunk(Fid *f)
569
{
570
	f->busy = 0;
571
	f->open = 0;
572
	free(f->user);
573
	paqFree(f->paq);
574
	return 0;
575
}
576
 
577
char *
578
rremove(Fid *f)
579
{
580
	rclunk(f);
581
	return Erdonly;
582
}
583
 
584
char *
585
rstat(Fid *f)
586
{
587
	if(f->busy == 0)
588
		return Enotexist;
589
	thdr.stat = (uchar*)thdr.data;
590
	thdr.nstat = packDir(f->paq->dir, thdr.stat, mesgsize);
591
	if(thdr.nstat == 0)
592
		return Edirtoobig;
593
	return 0;
594
}
595
 
596
char *
597
rwstat(Fid *f)
598
{
599
	if(f->busy == 0)
600
		return Enotexist;
601
	return Erdonly;
602
}
603
 
604
Paq*
605
paqCpy(Paq *s)
606
{
607
	s->ref++;
608
	return s;
609
}
610
 
611
void
612
paqFree(Paq *p)
613
{
614
	if(p == nil)
615
		return;
616
	p->ref--;
617
	if(p->ref > 0)
618
		return;
619
assert(p != root);
620
	paqFree(p->up);
621
	paqDirFree(p->dir);
622
	free(p);
623
}
624
 
625
void
626
paqDirFree(PaqDir *pd)
627
{
628
	if(pd == nil)
629
		return;
630
	free(pd->name);
631
	free(pd->uid);
632
	free(pd->gid);
633
	free(pd);
634
}
635
 
636
Qid
637
paqDirQid(PaqDir *d)
638
{
639
	Qid q;
640
 
641
	q.path = d->qid;
642
	q.vers = 0;
643
	q.type = d->mode >> 24;
644
 
645
	return q;
646
}
647
 
648
int
649
packDir(PaqDir *s, uchar *buf, int n)
650
{
651
	Dir dir;
652
 
653
	memset(&dir, 0, sizeof(dir));
654
	dir.qid = paqDirQid(s);
655
	dir.mode = s->mode;
656
	dir.atime = s->mtime;
657
	dir.mtime = s->mtime;
658
	dir.length = s->length;
659
	dir.name = s->name;
660
	dir.uid = s->uid;
661
	dir.gid = s->gid;
662
	dir.muid = s->uid;
663
 
664
	n = convD2M(&dir, buf, n);
665
	if(n < STATFIXLEN)
666
		return 0;
667
	return n;
668
}
669
 
670
Block *
671
blockLoad(ulong addr, int type)
672
{
673
	ulong age;
674
	int i, j;
675
	Block *b;
676
 
677
	if(addr == 0)
678
		return nil;
679
 
680
	cacheage++;
681
 
682
	/* age has wraped */
683
	if(cacheage == 0) {
684
		for(i=0; i<cachesize; i++)
685
			cache[i].age = 0;
686
	}
687
 
688
	j = -1;
689
	age = ~0;
690
	for(i=0; i<cachesize; i++) {
691
		b = &cache[i];
692
		if(b->age < age && b->ref == 0) {
693
			age = b->age;
694
			j = i;
695
		}
696
		if(b->addr != addr)
697
			continue;
698
		b->age = cacheage;
699
		b->ref++;
700
		return b;
701
	}
702
	if(j < 0)
703
		sysfatal("no empty spots in cache!");
704
	b = &cache[j];
705
	assert(b->ref == 0);
706
 
707
	if(!blockRead(b->data, addr, type)) {
708
		b->addr = 0;
709
		b->age = 0;
710
		return nil;
711
	}
712
 
713
	b->age = cacheage;
714
	b->addr = addr;
715
	b->ref = 1;
716
 
717
	return b;
718
}
719
 
720
void
721
blockFree(Block *b)
722
{
723
	if(b == nil)
724
		return;
725
	if(--b->ref > 0)
726
		return;
727
	assert(b->ref == 0);
728
}
729
 
730
Paq*
731
paqWalk(Paq *s, char *name)
732
{
733
	Block *ptr, *b;
734
	uchar *p, *ep;
735
	PaqDir *pd;
736
	int i, n;
737
	Paq *ss;
738
 
739
	if(strcmp(name, "..") == 0)
740
		return paqCpy(s->up);
741
 
742
	if(rootfile && s == root){
743
		if(strcmp(name, rootfile->dir->name) == 0)
744
			return paqCpy(rootfile);
745
		return nil;
746
	}
747
 
748
	ptr = blockLoad(s->dir->offset, PointerBlock);
749
	if(ptr == nil)
750
		return nil;
751
 
752
	for(i=0; i<blocksize/4; i++) {
753
		b = blockLoad(getl(ptr->data+i*4), DirBlock);
754
		if(b == nil)
755
			break;
756
		p = b->data;
757
		ep = p + blocksize;
758
		while(checkDirSize(p, ep)) {
759
			n = gets(p);
760
			pd = getDir(p);
761
			if(strcmp(pd->name, name) == 0) {
762
				ss = emallocz(sizeof(Paq));
763
				ss->ref = 1;
764
				ss->up = paqCpy(s);
765
				ss->dir = pd;
766
				ss->qid = paqDirQid(pd);
767
				blockFree(b);
768
				blockFree(ptr);
769
				return ss;
770
			}
771
			paqDirFree(pd);
772
			p += n;
773
		}
774
		blockFree(b);
775
	}
776
 
777
	blockFree(ptr);
778
	return nil;
779
}
780
 
781
Fid *
782
newfid(int fid)
783
{
784
	Fid *f, *ff;
785
 
786
	ff = 0;
787
	for(f = fids; f; f = f->next)
788
		if(f->fid == fid)
789
			return f;
790
		else if(!ff && !f->busy)
791
			ff = f;
792
	if(ff){
793
		ff->fid = fid;
794
		return ff;
795
	}
796
	f = emallocz(sizeof *f);
797
	f->fid = fid;
798
	f->next = fids;
799
	fids = f;
800
	return f;
801
}
802
 
803
void
804
io(int fd)
805
{
806
	char *err;
807
	int n, pid;
808
	uchar *mdata;
809
 
810
	mdata = emalloc(mesgsize);
811
 
812
	pid = getpid();
813
 
814
	for(;;){
815
		n = read9pmsg(fd, mdata, mesgsize);
816
		if(n < 0)
817
			sysfatal("mount read");
818
		if(n == 0)
819
			break;
820
		if(convM2S(mdata, n, &rhdr) == 0)
821
			continue;
822
 
823
		if(debug)
824
			fprint(2, "paqfs %d:<-%F\n", pid, &rhdr);
825
 
826
		thdr.data = (char*)mdata + IOHDRSZ;
827
		if(!fcalls[rhdr.type])
828
			err = "bad fcall type";
829
		else
830
			err = (*fcalls[rhdr.type])(newfid(rhdr.fid));
831
		if(err){
832
			thdr.type = Rerror;
833
			thdr.ename = err;
834
		}else{
835
			thdr.type = rhdr.type + 1;
836
			thdr.fid = rhdr.fid;
837
		}
838
		thdr.tag = rhdr.tag;
839
		if(debug)
840
			fprint(2, "paqfs %d:->%F\n", pid, &thdr);/**/
841
		n = convS2M(&thdr, mdata, mesgsize);
842
		if(n == 0)
843
			sysfatal("convS2M sysfatal on write");
844
		if(write(fd, mdata, n) != n)
845
			sysfatal("mount write");
846
	}
847
}
848
 
849
int
850
perm(PaqDir *s, char *user, int p)
851
{
852
	ulong perm = s->mode;
853
 
854
	if((p*Pother) & perm)
855
		return 1;
856
	if((noauth || strcmp(user, s->gid)==0) && ((p*Pgroup) & perm))
857
		return 1;
858
	if((noauth || strcmp(user, s->uid)==0) && ((p*Powner) & perm))
859
		return 1;
860
	return 0;
861
}
862
 
863
void
864
init(char *file, int verify)
865
{
866
	PaqHeader hdr;
867
	PaqTrailer tlr;
868
	Dir *dir;
869
	int i;
870
	uchar *p;
871
	DigestState *ds = nil;
872
	PaqDir *r;
873
	Block *b;
874
	ulong offset;
875
 
876
	inflateinit();
877
 
878
	bin = Bopen(file, OREAD);
879
	if(bin == nil)
880
		sysfatal("could not open file: %s: %r", file);
881
	if(verify)
882
		ds = sha1(0, 0, 0, 0);
883
 
884
	readHeader(&hdr, file, ds);
885
	blocksize = hdr.blocksize;
886
 
887
	if(verify) {
888
		readBlocks(file, ds);
889
	} else {
890
		dir = dirstat(file);
891
		if(dir == nil)
892
			sysfatal("could not stat file: %s: %r", file);
893
		offset = dir->length - TrailerSize;
894
		free(dir);
895
		if(Bseek(bin, offset, 0) != offset)
896
			sysfatal("could not seek to trailer: %s", file);
897
	}
898
 
899
	readTrailer(&tlr, file, ds);
900
 
901
	/* asctime includes a newline - yuk */
902
	if(!qflag){
903
		fprint(2, "%s: %s", hdr.label, asctime(gmtime(hdr.time)));
904
		fprint(2, "fingerprint: %V\n", tlr.sha1);
905
	}
906
 
907
	cache = emallocz(cachesize*sizeof(Block));
908
	p = emalloc(cachesize*blocksize);
909
	for(i=0; i<cachesize; i++) {
910
		cache[i].data = p;
911
		p += blocksize;
912
	}
913
 
914
	/* hand craft root */
915
	b = blockLoad(tlr.root, DirBlock);
916
	if(b == nil || !checkDirSize(b->data, b->data+blocksize))
917
		sysfatal("could not read root block: %s", file);
918
	r = getDir(b->data);
919
	blockFree(b);
920
	root = emallocz(sizeof(Paq));
921
	root->qid = paqDirQid(r);
922
	root->ref = 1;
923
	root->dir = r;
924
	root->up = root;	/* parent of root is root */
925
 
926
	/* craft root directory if root is a normal file */
927
	if(!(root->qid.type&QTDIR)){
928
		rootfile = root;
929
		root = emallocz(sizeof(Paq));
930
		root->qid = rootfile->qid;
931
		root->qid.type |= QTDIR;
932
		root->qid.path++;
933
		root->ref = 1;
934
		root->dir = emallocz(sizeof(PaqDir));
935
		*root->dir = *r;
936
		root->dir->mode |= DMDIR|0111;
937
		root->up = root;
938
	}
939
}
940
 
941
int
942
blockRead(uchar *data, ulong addr, int type)
943
{
944
	uchar buf[BlockSize];
945
	PaqBlock b;
946
	uchar *cdat;
947
 
948
	if(Bseek(bin, addr, 0) != addr){
949
		fprint(2, "paqfs: seek %lud: %r\n", addr);
950
		return 0;
951
	}
952
	if(Bread(bin, buf, BlockSize) != BlockSize){
953
		fprint(2, "paqfs: read %d at %lud: %r\n", BlockSize, addr);
954
		return 0;
955
	}
956
	getBlock(buf, &b);
957
	if(b.magic != BlockMagic || b.size > blocksize || b.type != type){
958
		fprint(2, "paqfs: bad block: magic %.8lux (want %.8ux) size %lud (max %d) type %ud (want %ud)\n",
959
			b.magic, BlockMagic, b.size, blocksize, b.type, type);
960
		return 0;
961
	}
962
 
963
	switch(b.encoding) {
964
	default:
965
		return 0;
966
	case NoEnc:
967
		if(Bread(bin, data, blocksize) < blocksize)
968
			return 0;
969
		break;
970
	case DeflateEnc:
971
		cdat = emalloc(b.size);
972
		if(Bread(bin, cdat, b.size) < b.size) {
973
			free(cdat);
974
			return 0;
975
		}
976
		if(inflateblock(data, blocksize, cdat, b.size) < 0) {
977
			fprint(2, "inflate error: %r\n");
978
			free(cdat);
979
			return 0;
980
		}
981
		free(cdat);
982
		break;
983
	}
984
	if(adler32(0, data, blocksize) != b.adler32)
985
		return 0;
986
	return 1;
987
}
988
 
989
void
990
readHeader(PaqHeader *hdr, char *name, DigestState *ds)
991
{
992
	uchar buf[HeaderSize];
993
 
994
	if(Bread(bin, buf, HeaderSize) < HeaderSize)
995
		sysfatal("could not read header: %s: %r", name);
996
	if(ds)
997
		sha1(buf, HeaderSize, 0, ds);
998
	getHeader(buf, hdr);
999
	if(hdr->magic != HeaderMagic)
1000
		sysfatal("bad header magic 0x%lux: %s", hdr->magic, name);
1001
	if(hdr->version != Version)
1002
		sysfatal("unknown file version: %s", name);
1003
}
1004
 
1005
void
1006
readBlocks(char *name, DigestState *ds)
1007
{
1008
	uchar *buf;
1009
	PaqBlock b;
1010
 
1011
	buf = emalloc(BlockSize+blocksize);
1012
 
1013
	for(;;) {
1014
		if(Bread(bin, buf, 4) < 4)
1015
			sysfatal("could not read block: %s: %r", name);
1016
		Bseek(bin, -4, 1);
1017
		/* check if it is a data block */
1018
 
1019
		if(getl(buf) != BlockMagic)
1020
			break;
1021
 
1022
		if(Bread(bin, buf, BlockSize) < BlockSize)
1023
			sysfatal("could not read block: %s: %r", name);
1024
		if(ds)
1025
			sha1(buf, BlockSize, 0, ds);
1026
		getBlock(buf, &b);
1027
 
1028
		if(b.size > blocksize)
1029
			sysfatal("bad block size: %lud: %s", b.size, name);
1030
		if(ds) {
1031
			if(Bread(bin, buf, b.size) < b.size)
1032
				sysfatal("sysfatal reading block: %s: %r", name);
1033
			sha1(buf, b.size, 0, ds);
1034
		} else
1035
			Bseek(bin, b.size, 1);
1036
	}
1037
 
1038
	free(buf);
1039
}
1040
 
1041
void
1042
readTrailer(PaqTrailer *tlr, char *name, DigestState *ds)
1043
{
1044
	uchar buf[TrailerSize];
1045
	uchar digest[SHA1dlen];
1046
 
1047
	if(Bread(bin, buf, TrailerSize) < TrailerSize)
1048
		sysfatal("could not read trailer: %s: %r", name);
1049
	getTrailer(buf, tlr);
1050
	if(tlr->magic != TrailerMagic)
1051
		sysfatal("bad trailer magic: %s", name);
1052
	if(ds) {
1053
		sha1(buf, TrailerSize-SHA1dlen, digest, ds);
1054
		if(memcmp(digest, tlr->sha1, SHA1dlen) != 0)
1055
			sysfatal("bad sha1 digest: %s", name);
1056
	}
1057
}
1058
 
1059
void *
1060
emalloc(ulong n)
1061
{
1062
	void *p;
1063
 
1064
	p = malloc(n);
1065
	if(!p)
1066
		sysfatal("out of memory");
1067
	return p;
1068
}
1069
 
1070
void *
1071
emallocz(ulong n)
1072
{
1073
	void *p;
1074
 
1075
	p = emalloc(n);
1076
	memset(p, 0, n);
1077
 
1078
	return p;
1079
}
1080
 
1081
void *
1082
erealloc(void *p, ulong n)
1083
{
1084
	p = realloc(p, n);
1085
	if(!p)
1086
		sysfatal("out of memory");
1087
	return p;
1088
}
1089
 
1090
char *
1091
estrdup(char *s)
1092
{
1093
	s = strdup(s);
1094
	if(s == nil)
1095
		sysfatal("out of memory");
1096
	return s;
1097
}
1098
 
1099
 
1100
ulong
1101
getl(uchar *p)
1102
{
1103
	return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
1104
}
1105
 
1106
 
1107
int
1108
gets(uchar *p)
1109
{
1110
	return (p[0]<<8) | p[1];
1111
}
1112
 
1113
int
1114
checkDirSize(uchar *p, uchar *ep)
1115
{
1116
	int n;	
1117
	int i;
1118
 
1119
	if(ep-p < 2)
1120
		return 0;
1121
	n = gets(p);
1122
	if(p+n > ep)
1123
		return 0;
1124
	ep = p+n;
1125
	p += 22;
1126
	for(i=0; i<3; i++) {
1127
		if(p+2 > ep)
1128
			return 0;
1129
		n = gets(p);
1130
		if(p+n > ep)
1131
			return 0;
1132
		p += n;
1133
	}
1134
	return 1;
1135
}
1136
 
1137
void
1138
getHeader(uchar *p, PaqHeader *h)
1139
{
1140
	h->magic = getl(p);
1141
	h->version = gets(p+4);
1142
	h->blocksize = gets(p+6);
1143
	if((h->magic>>16) == BigHeaderMagic){
1144
		h->magic = HeaderMagic;
1145
		h->version = gets(p+2);
1146
		h->blocksize = getl(p+4);
1147
	}
1148
	h->time = getl(p+8);
1149
	memmove(h->label, p+12, sizeof(h->label));
1150
	h->label[sizeof(h->label)-1] = 0;
1151
}
1152
 
1153
void
1154
getTrailer(uchar *p, PaqTrailer *t)
1155
{
1156
	t->magic = getl(p);
1157
	t->root = getl(p+4);
1158
	memmove(t->sha1, p+8, SHA1dlen);
1159
}
1160
 
1161
void
1162
getBlock(uchar *p, PaqBlock *b)
1163
{
1164
	b->magic = getl(p);
1165
	b->size = gets(p+4);
1166
	if((b->magic>>16) == BigBlockMagic){
1167
		b->magic = BlockMagic;
1168
		b->size = getl(p+2);
1169
	}
1170
	b->type = p[6];
1171
	b->encoding = p[7];
1172
	b->adler32 = getl(p+8);
1173
}
1174
 
1175
PaqDir *
1176
getDir(uchar *p)
1177
{
1178
	PaqDir *pd;
1179
 
1180
	pd = emallocz(sizeof(PaqDir));
1181
	pd->qid = getl(p+2);
1182
	pd->mode = getl(p+6);
1183
	pd->mtime = getl(p+10);
1184
	pd->length = getl(p+14);
1185
	pd->offset = getl(p+18);
1186
	p += 22;
1187
	pd->name = getstr(p);
1188
	p += gets(p);
1189
	pd->uid = getstr(p);
1190
	p += gets(p);
1191
	pd->gid = getstr(p);
1192
 
1193
	return pd;
1194
}
1195
 
1196
 
1197
char *
1198
getstr(uchar *p)
1199
{
1200
	char *s;
1201
	int n;
1202
 
1203
	n = gets(p);
1204
	s = emalloc(n+1);
1205
	memmove(s, p+2, n);
1206
	s[n] = 0;
1207
	return s;
1208
}
1209
 
1210
void
1211
usage(void)
1212
{
1213
	fprint(2, "usage: %s [-disv] [-c cachesize] [-m mountpoint] [-M mesgsize] paqfile\n", argv0);
1214
	exits("usage");
1215
}
1216