xref: /plan9/sys/src/cmd/paqfs/paqfs.c (revision f1a26d48345bb11f3a3fddaf7f7d35e9d4d8a55b)
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
sha1fmt(Fmt * f)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
main(int argc,char * argv[])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*
rversion(Fid *)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*
rauth(Fid *)281 rauth(Fid*)
282 {
283 	return Enoauth;
284 }
285 
286 char*
rflush(Fid * f)287 rflush(Fid *f)
288 {
289 	USED(f);
290 	return 0;
291 }
292 
293 char*
rattach(Fid * f)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*
clone(Fid * f,Fid ** res)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*
rwalk(Fid * f)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 *
ropen(Fid * f)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 *
rcreate(Fid * f)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 *
readdir(Fid * f)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*
rread(Fid * f)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*
rwrite(Fid * f)560 rwrite(Fid *f)
561 {
562 	if(f->busy == 0)
563 		return Enotexist;
564 	return Erdonly;
565 }
566 
567 char *
rclunk(Fid * f)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 *
rremove(Fid * f)578 rremove(Fid *f)
579 {
580 	rclunk(f);
581 	return Erdonly;
582 }
583 
584 char *
rstat(Fid * f)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 *
rwstat(Fid * f)597 rwstat(Fid *f)
598 {
599 	if(f->busy == 0)
600 		return Enotexist;
601 	return Erdonly;
602 }
603 
604 Paq*
paqCpy(Paq * s)605 paqCpy(Paq *s)
606 {
607 	s->ref++;
608 	return s;
609 }
610 
611 void
paqFree(Paq * p)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
paqDirFree(PaqDir * pd)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
paqDirQid(PaqDir * d)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
packDir(PaqDir * s,uchar * buf,int n)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 *
blockLoad(ulong addr,int type)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
blockFree(Block * b)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*
paqWalk(Paq * s,char * name)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 *
newfid(int 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
io(int fd)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
perm(PaqDir * s,char * user,int p)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
init(char * file,int verify)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
blockRead(uchar * data,ulong addr,int type)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
readHeader(PaqHeader * hdr,char * name,DigestState * ds)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
readBlocks(char * name,DigestState * ds)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
readTrailer(PaqTrailer * tlr,char * name,DigestState * ds)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 *
emalloc(ulong n)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 *
emallocz(ulong n)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 *
erealloc(void * p,ulong n)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 *
estrdup(char * s)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
getl(uchar * p)1101 getl(uchar *p)
1102 {
1103 	return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
1104 }
1105 
1106 
1107 int
gets(uchar * p)1108 gets(uchar *p)
1109 {
1110 	return (p[0]<<8) | p[1];
1111 }
1112 
1113 int
checkDirSize(uchar * p,uchar * ep)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
getHeader(uchar * p,PaqHeader * h)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
getTrailer(uchar * p,PaqTrailer * t)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
getBlock(uchar * p,PaqBlock * b)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 *
getDir(uchar * p)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 *
getstr(uchar * p)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
usage(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 
1217