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