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