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), DataBlock); 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 1217