fs.c (28255B)
1 #include "common.h" 2 #include <auth.h> 3 #include <fcall.h> 4 #include <libsec.h> 5 #include <9pclient.h> /* jpc */ 6 #include <thread.h> /* jpc */ 7 #include "dat.h" 8 9 enum 10 { 11 OPERM = 0x3, /* mask of all permission types in open mode */ 12 }; 13 14 typedef struct Fid Fid; 15 16 struct Fid 17 { 18 Qid qid; 19 short busy; 20 short open; 21 int fid; 22 Fid *next; 23 Mailbox *mb; 24 Message *m; 25 Message *mtop; /* top level message */ 26 27 /*finger pointers to speed up reads of large directories */ 28 long foff; /* offset/DIRLEN of finger */ 29 Message *fptr; /* pointer to message at off */ 30 int fvers; /* mailbox version when finger was saved */ 31 }; 32 33 ulong path; /* incremented for each new file */ 34 Fid *fids; 35 int mfd[2]; 36 char user[Elemlen]; 37 int messagesize = 4*1024*IOHDRSZ; 38 uchar mdata[8*1024*IOHDRSZ]; 39 uchar mbuf[8*1024*IOHDRSZ]; 40 Fcall thdr; 41 Fcall rhdr; 42 int fflg; 43 char *mntpt; 44 int biffing; 45 int plumbing = 1; 46 47 QLock mbllock; 48 Mailbox *mbl; 49 50 Fid *newfid(int); 51 void error(char*); 52 void io(void); 53 void *erealloc(void*, ulong); 54 void *emalloc(ulong); 55 void usage(void); 56 void run_io(void*); 57 void reader(void*); 58 int readheader(Message*, char*, int, int); 59 int cistrncmp(char*, char*, int); 60 int tokenconvert(String*, char*, int); 61 String* stringconvert(String*, char*, int); 62 void post(char*, char*, int); 63 64 char *rflush(Fid*), *rauth(Fid*), 65 *rattach(Fid*), *rwalk(Fid*), 66 *ropen(Fid*), *rcreate(Fid*), 67 *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*), 68 *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*), 69 *rversion(Fid*); 70 71 char *(*fcalls[])(Fid*) = { 72 [Tflush] rflush, 73 [Tversion] rversion, 74 [Tauth] rauth, 75 [Tattach] rattach, 76 [Twalk] rwalk, 77 [Topen] ropen, 78 [Tcreate] rcreate, 79 [Tread] rread, 80 [Twrite] rwrite, 81 [Tclunk] rclunk, 82 [Tremove] rremove, 83 [Tstat] rstat, 84 [Twstat] rwstat 85 }; 86 87 char Eperm[] = "permission denied"; 88 char Enotdir[] = "not a directory"; 89 char Enoauth[] = "upas/fs: authentication not required"; 90 char Enotexist[] = "file does not exist"; 91 char Einuse[] = "file in use"; 92 char Eexist[] = "file exists"; 93 char Enotowner[] = "not owner"; 94 char Eisopen[] = "file already open for I/O"; 95 char Excl[] = "exclusive use file already open"; 96 char Ename[] = "illegal name"; 97 char Ebadctl[] = "unknown control message"; 98 99 char *dirtab[] = 100 { 101 [Qdir] ".", 102 [Qbody] "body", 103 [Qbcc] "bcc", 104 [Qcc] "cc", 105 [Qdate] "date", 106 [Qdigest] "digest", 107 [Qdisposition] "disposition", 108 [Qfilename] "filename", 109 [Qfrom] "from", 110 [Qheader] "header", 111 [Qinfo] "info", 112 [Qinreplyto] "inreplyto", 113 [Qlines] "lines", 114 [Qmimeheader] "mimeheader", 115 [Qmessageid] "messageid", 116 [Qraw] "raw", 117 [Qrawunix] "rawunix", 118 [Qrawbody] "rawbody", 119 [Qrawheader] "rawheader", 120 [Qreplyto] "replyto", 121 [Qsender] "sender", 122 [Qsubject] "subject", 123 [Qto] "to", 124 [Qtype] "type", 125 [Qunixdate] "unixdate", 126 [Qunixheader] "unixheader", 127 [Qctl] "ctl", 128 [Qmboxctl] "ctl" 129 }; 130 131 enum 132 { 133 Hsize= 1277 134 }; 135 136 Hash *htab[Hsize]; 137 138 int debug; 139 int fflag; 140 int logging; 141 142 void 143 usage(void) 144 { 145 fprint(2, "usage: %s [-b -m mountpoint]\n", argv0); 146 threadexits("usage"); 147 } 148 149 void 150 notifyf(void *a, char *s) 151 { 152 USED(a); 153 if(strncmp(s, "interrupt", 9) == 0) 154 noted(NCONT); 155 noted(NDFLT); 156 } 157 158 int 159 threadmaybackground(void) 160 { 161 return 1; 162 } 163 164 void 165 threadmain(int argc, char *argv[]) 166 { 167 int p[2], std, nodflt; 168 char maildir[128]; 169 char mbox[128]; 170 char *mboxfile, *err; 171 char srvfile[64]; 172 int srvpost; 173 174 rfork(RFNOTEG); 175 mntpt = nil; 176 fflag = 0; 177 mboxfile = nil; 178 std = 0; 179 nodflt = 0; 180 srvpost = 0; 181 182 ARGBEGIN{ 183 case 'b': 184 biffing = 1; 185 break; 186 case 'f': 187 fflag = 1; 188 mboxfile = ARGF(); 189 break; 190 case 'm': 191 mntpt = ARGF(); 192 break; 193 case 'd': 194 debug = 1; 195 break; 196 case 'p': 197 plumbing = 0; 198 break; 199 case 's': 200 srvpost = 1; 201 break; 202 case 'l': 203 logging = 1; 204 break; 205 case 'n': 206 nodflt = 1; 207 break; 208 default: 209 usage(); 210 }ARGEND 211 212 if(pipe(p) < 0) 213 error("pipe failed"); 214 mfd[0] = p[0]; 215 mfd[1] = p[0]; 216 217 notify(notifyf); 218 strcpy(user, getuser()); 219 if(mntpt == nil){ 220 snprint(maildir, sizeof(maildir), "/mail/fs"); 221 mntpt = maildir; 222 } 223 if(mboxfile == nil && !nodflt){ 224 snprint(mbox, sizeof(mbox), "/mail/box/%s/mbox", user); 225 mboxfile = mbox; 226 std = 1; 227 } 228 229 if(debug) 230 fmtinstall('F', fcallfmt); 231 232 if(mboxfile != nil){ 233 err = newmbox(mboxfile, "mbox", std); 234 if(err != nil) 235 sysfatal("opening mailbox: %s", err); 236 } 237 238 switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){ /* jpc removed RFEND */ 239 case -1: 240 error("fork"); 241 case 0: 242 henter(PATH(0, Qtop), dirtab[Qctl], 243 (Qid){PATH(0, Qctl), 0, QTFILE}, nil, nil); 244 close(p[1]); 245 io(); 246 postnote(PNGROUP, getpid(), "die yankee pig dog"); 247 break; 248 default: 249 close(p[0]); /* don't deadlock if child fails */ 250 if(srvpost){ 251 sprint(srvfile, "/srv/upasfs.%s", user); 252 /* post(srvfile, "upasfs", p[1]); jpc */ 253 post9pservice(p[1], "upasfs", nil); /* jpc */ 254 } else { 255 error("tried to mount, fixme"); /* jpc */ 256 /* if(mount(p[1], -1, mntpt, MREPL, "") < 0) 257 error("mount failed"); jpc */ 258 } 259 } 260 threadexits(0); 261 } 262 263 void run_io(void *v) { 264 int *p; 265 266 p = v; 267 henter(PATH(0, Qtop), dirtab[Qctl], 268 (Qid){PATH(0, Qctl), 0, QTFILE}, nil, nil); 269 close(p[1]); 270 io(); 271 postnote(PNGROUP, getpid(), "die yankee pig dog"); 272 } 273 274 static int 275 fileinfo(Message *m, int t, char **pp) 276 { 277 char *p; 278 int len; 279 280 p = ""; 281 len = 0; 282 switch(t){ 283 case Qbody: 284 p = m->body; 285 len = m->bend - m->body; 286 break; 287 case Qbcc: 288 if(m->bcc822){ 289 p = s_to_c(m->bcc822); 290 len = strlen(p); 291 } 292 break; 293 case Qcc: 294 if(m->cc822){ 295 p = s_to_c(m->cc822); 296 len = strlen(p); 297 } 298 break; 299 case Qdisposition: 300 switch(m->disposition){ 301 case Dinline: 302 p = "inline"; 303 break; 304 case Dfile: 305 p = "file"; 306 break; 307 } 308 len = strlen(p); 309 break; 310 case Qdate: 311 if(m->date822){ 312 p = s_to_c(m->date822); 313 len = strlen(p); 314 } else if(m->unixdate != nil){ 315 p = s_to_c(m->unixdate); 316 len = strlen(p); 317 } 318 break; 319 case Qfilename: 320 if(m->filename){ 321 p = s_to_c(m->filename); 322 len = strlen(p); 323 } 324 break; 325 case Qinreplyto: 326 if(m->inreplyto822){ 327 p = s_to_c(m->inreplyto822); 328 len = strlen(p); 329 } 330 break; 331 case Qmessageid: 332 if(m->messageid822){ 333 p = s_to_c(m->messageid822); 334 len = strlen(p); 335 } 336 break; 337 case Qfrom: 338 if(m->from822){ 339 p = s_to_c(m->from822); 340 len = strlen(p); 341 } else if(m->unixfrom != nil){ 342 p = s_to_c(m->unixfrom); 343 len = strlen(p); 344 } 345 break; 346 case Qheader: 347 p = m->header; 348 len = headerlen(m); 349 break; 350 case Qlines: 351 p = m->lines; 352 if(*p == 0) 353 countlines(m); 354 len = strlen(m->lines); 355 break; 356 case Qraw: 357 p = m->start; 358 if(strncmp(m->start, "From ", 5) == 0){ 359 p = strchr(p, '\n'); 360 if(p == nil) 361 p = m->start; 362 else 363 p++; 364 } 365 len = m->end - p; 366 break; 367 case Qrawunix: 368 p = m->start; 369 len = m->end - p; 370 break; 371 case Qrawbody: 372 p = m->rbody; 373 len = m->rbend - p; 374 break; 375 case Qrawheader: 376 p = m->header; 377 len = m->hend - p; 378 break; 379 case Qmimeheader: 380 p = m->mheader; 381 len = m->mhend - p; 382 break; 383 case Qreplyto: 384 p = nil; 385 if(m->replyto822 != nil){ 386 p = s_to_c(m->replyto822); 387 len = strlen(p); 388 } else if(m->from822 != nil){ 389 p = s_to_c(m->from822); 390 len = strlen(p); 391 } else if(m->sender822 != nil){ 392 p = s_to_c(m->sender822); 393 len = strlen(p); 394 } else if(m->unixfrom != nil){ 395 p = s_to_c(m->unixfrom); 396 len = strlen(p); 397 } 398 break; 399 case Qsender: 400 if(m->sender822){ 401 p = s_to_c(m->sender822); 402 len = strlen(p); 403 } 404 break; 405 case Qsubject: 406 p = nil; 407 if(m->subject822){ 408 p = s_to_c(m->subject822); 409 len = strlen(p); 410 } 411 break; 412 case Qto: 413 if(m->to822){ 414 p = s_to_c(m->to822); 415 len = strlen(p); 416 } 417 break; 418 case Qtype: 419 if(m->type){ 420 p = s_to_c(m->type); 421 len = strlen(p); 422 } 423 break; 424 case Qunixdate: 425 if(m->unixdate){ 426 p = s_to_c(m->unixdate); 427 len = strlen(p); 428 } 429 break; 430 case Qunixheader: 431 if(m->unixheader){ 432 p = s_to_c(m->unixheader); 433 len = s_len(m->unixheader); 434 } 435 break; 436 case Qdigest: 437 if(m->sdigest){ 438 p = s_to_c(m->sdigest); 439 len = strlen(p); 440 } 441 break; 442 } 443 *pp = p; 444 return len; 445 } 446 447 int infofields[] = { 448 Qfrom, 449 Qto, 450 Qcc, 451 Qreplyto, 452 Qunixdate, 453 Qsubject, 454 Qtype, 455 Qdisposition, 456 Qfilename, 457 Qdigest, 458 Qbcc, 459 Qinreplyto, 460 Qdate, 461 Qsender, 462 Qmessageid, 463 Qlines, 464 -1 465 }; 466 467 static int 468 readinfo(Message *m, char *buf, long off, int count) 469 { 470 char *p; 471 int len, i, n; 472 String *s; 473 474 s = s_new(); 475 len = 0; 476 for(i = 0; len < count && infofields[i] >= 0; i++){ 477 n = fileinfo(m, infofields[i], &p); 478 s = stringconvert(s, p, n); 479 s_append(s, "\n"); 480 p = s_to_c(s); 481 n = strlen(p); 482 if(off > 0){ 483 if(off >= n){ 484 off -= n; 485 continue; 486 } 487 p += off; 488 n -= off; 489 off = 0; 490 } 491 if(n > count - len) 492 n = count - len; 493 if(buf) 494 memmove(buf+len, p, n); 495 len += n; 496 } 497 s_free(s); 498 return len; 499 } 500 501 static void 502 mkstat(Dir *d, Mailbox *mb, Message *m, int t) 503 { 504 char *p; 505 506 d->uid = user; 507 d->gid = user; 508 d->muid = user; 509 d->mode = 0444; 510 d->qid.vers = 0; 511 d->qid.type = QTFILE; 512 d->type = 0; 513 d->dev = 0; 514 if(mb != nil && mb->d != nil){ 515 d->atime = mb->d->atime; 516 d->mtime = mb->d->mtime; 517 } else { 518 d->atime = time(0); 519 d->mtime = d->atime; 520 } 521 522 switch(t){ 523 case Qtop: 524 d->name = "."; 525 d->mode = DMDIR|0555; 526 d->atime = d->mtime = time(0); 527 d->length = 0; 528 d->qid.path = PATH(0, Qtop); 529 d->qid.type = QTDIR; 530 break; 531 case Qmbox: 532 d->name = mb->name; 533 d->mode = DMDIR|0555; 534 d->length = 0; 535 d->qid.path = PATH(mb->id, Qmbox); 536 d->qid.type = QTDIR; 537 d->qid.vers = mb->vers; 538 break; 539 case Qdir: 540 d->name = m->name; 541 d->mode = DMDIR|0555; 542 d->length = 0; 543 d->qid.path = PATH(m->id, Qdir); 544 d->qid.type = QTDIR; 545 break; 546 case Qctl: 547 d->name = dirtab[t]; 548 d->mode = 0666; 549 d->atime = d->mtime = time(0); 550 d->length = 0; 551 d->qid.path = PATH(0, Qctl); 552 break; 553 case Qmboxctl: 554 d->name = dirtab[t]; 555 d->mode = 0222; 556 d->atime = d->mtime = time(0); 557 d->length = 0; 558 d->qid.path = PATH(mb->id, Qmboxctl); 559 break; 560 case Qinfo: 561 d->name = dirtab[t]; 562 d->length = readinfo(m, nil, 0, 1<<30); 563 d->qid.path = PATH(m->id, t); 564 break; 565 default: 566 d->name = dirtab[t]; 567 d->length = fileinfo(m, t, &p); 568 d->qid.path = PATH(m->id, t); 569 break; 570 } 571 } 572 573 char* 574 rversion(Fid* dummy) 575 { 576 Fid *f; 577 578 if(thdr.msize < 256) 579 return "max messagesize too small"; 580 if(thdr.msize < messagesize) 581 messagesize = thdr.msize; 582 rhdr.msize = messagesize; 583 if(strncmp(thdr.version, "9P2000", 6) != 0) 584 return "unknown 9P version"; 585 else 586 rhdr.version = "9P2000"; 587 for(f = fids; f; f = f->next) 588 if(f->busy) 589 rclunk(f); 590 return nil; 591 } 592 593 char* 594 rauth(Fid* dummy) 595 { 596 return Enoauth; 597 } 598 599 char* 600 rflush(Fid *f) 601 { 602 USED(f); 603 return 0; 604 } 605 606 char* 607 rattach(Fid *f) 608 { 609 f->busy = 1; 610 f->m = nil; 611 f->mb = nil; 612 f->qid.path = PATH(0, Qtop); 613 f->qid.type = QTDIR; 614 f->qid.vers = 0; 615 rhdr.qid = f->qid; 616 if(strcmp(thdr.uname, user) != 0) 617 return Eperm; 618 return 0; 619 } 620 621 static Fid* 622 doclone(Fid *f, int nfid) 623 { 624 Fid *nf; 625 626 nf = newfid(nfid); 627 if(nf->busy) 628 return nil; 629 nf->busy = 1; 630 nf->open = 0; 631 nf->m = f->m; 632 nf->mtop = f->mtop; 633 nf->mb = f->mb; 634 if(f->mb != nil) 635 mboxincref(f->mb); 636 if(f->mtop != nil){ 637 qlock(&f->mb->ql); 638 msgincref(f->mtop); 639 qunlock(&f->mb->ql); 640 } 641 nf->qid = f->qid; 642 return nf; 643 } 644 645 char* 646 dowalk(Fid *f, char *name) 647 { 648 int t; 649 Mailbox *omb, *mb; 650 char *rv, *p; 651 Hash *h; 652 653 t = FILE(f->qid.path); 654 655 rv = Enotexist; 656 657 omb = f->mb; 658 if(omb) 659 qlock(&omb->ql); 660 else 661 qlock(&mbllock); 662 663 /* this must catch everything except . and .. */ 664 retry: 665 h = hlook(f->qid.path, name); 666 if(h != nil){ 667 f->mb = h->mb; 668 f->m = h->m; 669 switch(t){ 670 case Qtop: 671 if(f->mb != nil) 672 mboxincref(f->mb); 673 break; 674 case Qmbox: 675 if(f->m){ 676 msgincref(f->m); 677 f->mtop = f->m; 678 } 679 break; 680 } 681 f->qid = h->qid; 682 rv = nil; 683 } else if((p = strchr(name, '.')) != nil && *name != '.'){ 684 *p = 0; 685 goto retry; 686 } 687 688 if(omb) 689 qunlock(&omb->ql); 690 else 691 qunlock(&mbllock); 692 if(rv == nil) 693 return rv; 694 695 if(strcmp(name, ".") == 0) 696 return nil; 697 698 if(f->qid.type != QTDIR) 699 return Enotdir; 700 701 if(strcmp(name, "..") == 0){ 702 switch(t){ 703 case Qtop: 704 f->qid.path = PATH(0, Qtop); 705 f->qid.type = QTDIR; 706 f->qid.vers = 0; 707 break; 708 case Qmbox: 709 f->qid.path = PATH(0, Qtop); 710 f->qid.type = QTDIR; 711 f->qid.vers = 0; 712 qlock(&mbllock); 713 mb = f->mb; 714 f->mb = nil; 715 mboxdecref(mb); 716 qunlock(&mbllock); 717 break; 718 case Qdir: 719 qlock(&f->mb->ql); 720 if(f->m->whole == f->mb->root){ 721 f->qid.path = PATH(f->mb->id, Qmbox); 722 f->qid.type = QTDIR; 723 f->qid.vers = f->mb->d->qid.vers; 724 msgdecref(f->mb, f->mtop); 725 f->m = f->mtop = nil; 726 } else { 727 f->m = f->m->whole; 728 f->qid.path = PATH(f->m->id, Qdir); 729 f->qid.type = QTDIR; 730 } 731 qunlock(&f->mb->ql); 732 break; 733 } 734 rv = nil; 735 } 736 return rv; 737 } 738 739 char* 740 rwalk(Fid *f) 741 { 742 Fid *nf; 743 char *rv; 744 int i; 745 746 if(f->open) 747 return Eisopen; 748 749 rhdr.nwqid = 0; 750 nf = nil; 751 752 /* clone if requested */ 753 if(thdr.newfid != thdr.fid){ 754 nf = doclone(f, thdr.newfid); 755 if(nf == nil) 756 return "new fid in use"; 757 f = nf; 758 } 759 760 /* if it's just a clone, return */ 761 if(thdr.nwname == 0 && nf != nil) 762 return nil; 763 764 /* walk each element */ 765 rv = nil; 766 for(i = 0; i < thdr.nwname; i++){ 767 rv = dowalk(f, thdr.wname[i]); 768 if(rv != nil){ 769 if(nf != nil) 770 rclunk(nf); 771 break; 772 } 773 rhdr.wqid[i] = f->qid; 774 } 775 rhdr.nwqid = i; 776 777 /* we only error out if no walk */ 778 if(i > 0) 779 rv = nil; 780 781 return rv; 782 } 783 784 char * 785 ropen(Fid *f) 786 { 787 int file; 788 789 if(f->open) 790 return Eisopen; 791 792 file = FILE(f->qid.path); 793 if(thdr.mode != OREAD) 794 if(file != Qctl && file != Qmboxctl) 795 return Eperm; 796 797 /* make sure we've decoded */ 798 if(file == Qbody){ 799 if(f->m->decoded == 0) 800 decode(f->m); 801 if(f->m->converted == 0) 802 convert(f->m); 803 } 804 805 rhdr.iounit = 0; 806 rhdr.qid = f->qid; 807 f->open = 1; 808 return 0; 809 } 810 811 char * 812 rcreate(Fid* dummy) 813 { 814 return Eperm; 815 } 816 817 int 818 readtopdir(Fid* dummy, uchar *buf, long off, int cnt, int blen) 819 { 820 Dir d; 821 int m, n; 822 long pos; 823 Mailbox *mb; 824 825 n = 0; 826 pos = 0; 827 mkstat(&d, nil, nil, Qctl); 828 m = convD2M(&d, &buf[n], blen); 829 if(off <= pos){ 830 if(m <= BIT16SZ || m > cnt) 831 return 0; 832 n += m; 833 cnt -= m; 834 } 835 pos += m; 836 837 for(mb = mbl; mb != nil; mb = mb->next){ 838 mkstat(&d, mb, nil, Qmbox); 839 m = convD2M(&d, &buf[n], blen-n); 840 if(off <= pos){ 841 if(m <= BIT16SZ || m > cnt) 842 break; 843 n += m; 844 cnt -= m; 845 } 846 pos += m; 847 } 848 return n; 849 } 850 851 int 852 readmboxdir(Fid *f, uchar *buf, long off, int cnt, int blen) 853 { 854 Dir d; 855 int n, m; 856 long pos; 857 Message *msg; 858 859 n = 0; 860 if(f->mb->ctl){ 861 mkstat(&d, f->mb, nil, Qmboxctl); 862 m = convD2M(&d, &buf[n], blen); 863 if(off == 0){ 864 if(m <= BIT16SZ || m > cnt){ 865 f->fptr = nil; 866 return 0; 867 } 868 n += m; 869 cnt -= m; 870 } else 871 off -= m; 872 } 873 874 /* to avoid n**2 reads of the directory, use a saved finger pointer */ 875 if(f->mb->vers == f->fvers && off >= f->foff && f->fptr != nil){ 876 msg = f->fptr; 877 pos = f->foff; 878 } else { 879 msg = f->mb->root->part; 880 pos = 0; 881 } 882 883 for(; cnt > 0 && msg != nil; msg = msg->next){ 884 /* act like deleted files aren't there */ 885 if(msg->deleted) 886 continue; 887 888 mkstat(&d, f->mb, msg, Qdir); 889 m = convD2M(&d, &buf[n], blen-n); 890 if(off <= pos){ 891 if(m <= BIT16SZ || m > cnt) 892 break; 893 n += m; 894 cnt -= m; 895 } 896 pos += m; 897 } 898 899 /* save a finger pointer for next read of the mbox directory */ 900 f->foff = pos; 901 f->fptr = msg; 902 f->fvers = f->mb->vers; 903 904 return n; 905 } 906 907 int 908 readmsgdir(Fid *f, uchar *buf, long off, int cnt, int blen) 909 { 910 Dir d; 911 int i, n, m; 912 long pos; 913 Message *msg; 914 915 n = 0; 916 pos = 0; 917 for(i = 0; i < Qmax; i++){ 918 mkstat(&d, f->mb, f->m, i); 919 m = convD2M(&d, &buf[n], blen-n); 920 if(off <= pos){ 921 if(m <= BIT16SZ || m > cnt) 922 return n; 923 n += m; 924 cnt -= m; 925 } 926 pos += m; 927 } 928 for(msg = f->m->part; msg != nil; msg = msg->next){ 929 mkstat(&d, f->mb, msg, Qdir); 930 m = convD2M(&d, &buf[n], blen-n); 931 if(off <= pos){ 932 if(m <= BIT16SZ || m > cnt) 933 break; 934 n += m; 935 cnt -= m; 936 } 937 pos += m; 938 } 939 940 return n; 941 } 942 943 char* 944 rread(Fid *f) 945 { 946 long off; 947 int t, i, n, cnt; 948 char *p; 949 950 rhdr.count = 0; 951 off = thdr.offset; 952 cnt = thdr.count; 953 954 if(cnt > messagesize - IOHDRSZ) 955 cnt = messagesize - IOHDRSZ; 956 957 rhdr.data = (char*)mbuf; 958 959 t = FILE(f->qid.path); 960 if(f->qid.type & QTDIR){ 961 if(t == Qtop) { 962 qlock(&mbllock); 963 n = readtopdir(f, mbuf, off, cnt, messagesize - IOHDRSZ); 964 qunlock(&mbllock); 965 } else if(t == Qmbox) { 966 qlock(&f->mb->ql); 967 if(off == 0) 968 syncmbox(f->mb, 1); 969 n = readmboxdir(f, mbuf, off, cnt, messagesize - IOHDRSZ); 970 qunlock(&f->mb->ql); 971 } else if(t == Qmboxctl) { 972 n = 0; 973 } else { 974 n = readmsgdir(f, mbuf, off, cnt, messagesize - IOHDRSZ); 975 } 976 977 rhdr.count = n; 978 return nil; 979 } 980 981 if(FILE(f->qid.path) == Qheader){ 982 rhdr.count = readheader(f->m, (char*)mbuf, off, cnt); 983 return nil; 984 } 985 986 if(FILE(f->qid.path) == Qinfo){ 987 rhdr.count = readinfo(f->m, (char*)mbuf, off, cnt); 988 return nil; 989 } 990 991 i = fileinfo(f->m, FILE(f->qid.path), &p); 992 if(off < i){ 993 if((off + cnt) > i) 994 cnt = i - off; 995 memmove(mbuf, p + off, cnt); 996 rhdr.count = cnt; 997 } 998 return nil; 999 } 1000 1001 char* 1002 rwrite(Fid *f) 1003 { 1004 char *err; 1005 char *token[1024]; 1006 int t, n; 1007 String *file; 1008 1009 t = FILE(f->qid.path); 1010 rhdr.count = thdr.count; 1011 switch(t){ 1012 case Qctl: 1013 if(thdr.count == 0) 1014 return Ebadctl; 1015 if(thdr.data[thdr.count-1] == '\n') 1016 thdr.data[thdr.count-1] = 0; 1017 else 1018 thdr.data[thdr.count] = 0; 1019 n = tokenize(thdr.data, token, nelem(token)); 1020 if(n == 0) 1021 return Ebadctl; 1022 if(strcmp(token[0], "open") == 0){ 1023 file = s_new(); 1024 switch(n){ 1025 case 1: 1026 err = Ebadctl; 1027 break; 1028 case 2: 1029 mboxpath(token[1], getlog(), file, 0); 1030 err = newmbox(s_to_c(file), nil, 0); 1031 break; 1032 default: 1033 mboxpath(token[1], getlog(), file, 0); 1034 if(strchr(token[2], '/') != nil) 1035 err = "/ not allowed in mailbox name"; 1036 else 1037 err = newmbox(s_to_c(file), token[2], 0); 1038 break; 1039 } 1040 s_free(file); 1041 return err; 1042 } 1043 if(strcmp(token[0], "close") == 0){ 1044 if(n < 2) 1045 return nil; 1046 freembox(token[1]); 1047 return nil; 1048 } 1049 if(strcmp(token[0], "delete") == 0){ 1050 if(n < 3) 1051 return nil; 1052 delmessages(n-1, &token[1]); 1053 return nil; 1054 } 1055 return Ebadctl; 1056 case Qmboxctl: 1057 if(f->mb && f->mb->ctl){ 1058 if(thdr.count == 0) 1059 return Ebadctl; 1060 if(thdr.data[thdr.count-1] == '\n') 1061 thdr.data[thdr.count-1] = 0; 1062 else 1063 thdr.data[thdr.count] = 0; 1064 n = tokenize(thdr.data, token, nelem(token)); 1065 if(n == 0) 1066 return Ebadctl; 1067 return (*f->mb->ctl)(f->mb, n, token); 1068 } 1069 } 1070 return Eperm; 1071 } 1072 1073 char * 1074 rclunk(Fid *f) 1075 { 1076 Mailbox *mb; 1077 1078 f->busy = 0; 1079 f->open = 0; 1080 if(f->mtop != nil){ 1081 qlock(&f->mb->ql); 1082 msgdecref(f->mb, f->mtop); 1083 qunlock(&f->mb->ql); 1084 } 1085 f->m = f->mtop = nil; 1086 mb = f->mb; 1087 if(mb != nil){ 1088 f->mb = nil; 1089 assert(mb->refs > 0); 1090 qlock(&mbllock); 1091 mboxdecref(mb); 1092 qunlock(&mbllock); 1093 } 1094 f->fid = -1; 1095 return 0; 1096 } 1097 1098 char * 1099 rremove(Fid *f) 1100 { 1101 if(f->m != nil){ 1102 if(f->m->deleted == 0) 1103 mailplumb(f->mb, f->m, 1); 1104 f->m->deleted = 1; 1105 } 1106 return rclunk(f); 1107 } 1108 1109 char * 1110 rstat(Fid *f) 1111 { 1112 Dir d; 1113 1114 if(FILE(f->qid.path) == Qmbox){ 1115 qlock(&f->mb->ql); 1116 syncmbox(f->mb, 1); 1117 qunlock(&f->mb->ql); 1118 } 1119 mkstat(&d, f->mb, f->m, FILE(f->qid.path)); 1120 rhdr.nstat = convD2M(&d, mbuf, messagesize - IOHDRSZ); 1121 rhdr.stat = mbuf; 1122 return 0; 1123 } 1124 1125 char * 1126 rwstat(Fid* dummy) 1127 { 1128 return Eperm; 1129 } 1130 1131 Fid * 1132 newfid(int fid) 1133 { 1134 Fid *f, *ff; 1135 1136 ff = 0; 1137 for(f = fids; f; f = f->next) 1138 if(f->fid == fid) 1139 return f; 1140 else if(!ff && !f->busy) 1141 ff = f; 1142 if(ff){ 1143 ff->fid = fid; 1144 ff->fptr = nil; 1145 return ff; 1146 } 1147 f = emalloc(sizeof *f); 1148 f->fid = fid; 1149 f->fptr = nil; 1150 f->next = fids; 1151 fids = f; 1152 return f; 1153 } 1154 1155 int 1156 fidmboxrefs(Mailbox *mb) 1157 { 1158 Fid *f; 1159 int refs = 0; 1160 1161 for(f = fids; f; f = f->next){ 1162 if(f->mb == mb) 1163 refs++; 1164 } 1165 return refs; 1166 } 1167 1168 void 1169 io(void) 1170 { 1171 char *err; 1172 int n, nw; 1173 1174 /* start a process to watch the mailboxes*/ 1175 if(plumbing){ 1176 proccreate(reader, nil, 16000); 1177 #if 0 /* jpc */ 1178 switch(rfork(RFPROC|RFMEM)){ 1179 case -1: 1180 /* oh well */ 1181 break; 1182 case 0: 1183 reader(); 1184 threadexits(nil); 1185 default: 1186 break; 1187 } 1188 #endif /* jpc */ 1189 } 1190 1191 for(;;){ 1192 /* 1193 * reading from a pipe or a network device 1194 * will give an error after a few eof reads 1195 * however, we cannot tell the difference 1196 * between a zero-length read and an interrupt 1197 * on the processes writing to us, 1198 * so we wait for the error 1199 */ 1200 checkmboxrefs(); 1201 n = read9pmsg(mfd[0], mdata, messagesize); 1202 if(n == 0) 1203 continue; 1204 if(n < 0) 1205 return; 1206 if(convM2S(mdata, n, &thdr) == 0) 1207 continue; 1208 1209 if(debug) 1210 fprint(2, "%s:<-%F\n", argv0, &thdr); 1211 1212 rhdr.data = (char*)mdata + messagesize; 1213 if(!fcalls[thdr.type]) 1214 err = "bad fcall type"; 1215 else 1216 err = (*fcalls[thdr.type])(newfid(thdr.fid)); 1217 if(err){ 1218 rhdr.type = Rerror; 1219 rhdr.ename = err; 1220 }else{ 1221 rhdr.type = thdr.type + 1; 1222 rhdr.fid = thdr.fid; 1223 } 1224 rhdr.tag = thdr.tag; 1225 if(debug) 1226 fprint(2, "%s:->%F\n", argv0, &rhdr);/**/ 1227 n = convS2M(&rhdr, mdata, messagesize); 1228 if((nw = write(mfd[1], mdata, n)) != n) { 1229 fprint(2,"wrote %d bytes\n",nw); 1230 error("mount write"); 1231 } 1232 } 1233 } 1234 1235 void 1236 reader(void *dummy) 1237 { 1238 ulong t; 1239 Dir *d; 1240 Mailbox *mb; 1241 1242 sleep(15*1000); 1243 for(;;){ 1244 t = time(0); 1245 qlock(&mbllock); 1246 for(mb = mbl; mb != nil; mb = mb->next){ 1247 assert(mb->refs > 0); 1248 if(mb->waketime != 0 && t > mb->waketime){ 1249 qlock(&mb->ql); 1250 mb->waketime = 0; 1251 break; 1252 } 1253 1254 d = dirstat(mb->path); 1255 if(d == nil) 1256 continue; 1257 1258 qlock(&mb->ql); 1259 if(mb->d) 1260 if(d->qid.path != mb->d->qid.path 1261 || d->qid.vers != mb->d->qid.vers){ 1262 free(d); 1263 break; 1264 } 1265 qunlock(&mb->ql); 1266 free(d); 1267 } 1268 qunlock(&mbllock); 1269 if(mb != nil){ 1270 syncmbox(mb, 1); 1271 qunlock(&mb->ql); 1272 } else 1273 sleep(15*1000); 1274 } 1275 } 1276 1277 int 1278 newid(void) 1279 { 1280 int rv; 1281 static int id; 1282 static Lock idlock; 1283 1284 lock(&idlock); 1285 rv = ++id; 1286 unlock(&idlock); 1287 1288 return rv; 1289 } 1290 1291 void 1292 error(char *s) 1293 { 1294 postnote(PNGROUP, getpid(), "die yankee pig dog"); 1295 fprint(2, "%s: %s: %r\n", argv0, s); 1296 threadexits(s); 1297 } 1298 1299 1300 typedef struct Ignorance Ignorance; 1301 struct Ignorance 1302 { 1303 Ignorance *next; 1304 char *str; /* string */ 1305 int partial; /* true if not exact match */ 1306 }; 1307 Ignorance *ignorance; 1308 1309 /* 1310 * read the file of headers to ignore 1311 */ 1312 void 1313 readignore(void) 1314 { 1315 char *p; 1316 Ignorance *i; 1317 Biobuf *b; 1318 1319 if(ignorance != nil) 1320 return; 1321 1322 b = Bopen("/mail/lib/ignore", OREAD); 1323 if(b == 0) 1324 return; 1325 while(p = Brdline(b, '\n')){ 1326 p[Blinelen(b)-1] = 0; 1327 while(*p && (*p == ' ' || *p == '\t')) 1328 p++; 1329 if(*p == '#') 1330 continue; 1331 i = malloc(sizeof(Ignorance)); 1332 if(i == 0) 1333 break; 1334 i->partial = strlen(p); 1335 i->str = strdup(p); 1336 if(i->str == 0){ 1337 free(i); 1338 break; 1339 } 1340 i->next = ignorance; 1341 ignorance = i; 1342 } 1343 Bterm(b); 1344 } 1345 1346 int 1347 ignore(char *p) 1348 { 1349 Ignorance *i; 1350 1351 readignore(); 1352 for(i = ignorance; i != nil; i = i->next) 1353 if(cistrncmp(i->str, p, i->partial) == 0) 1354 return 1; 1355 return 0; 1356 } 1357 1358 int 1359 hdrlen(char *p, char *e) 1360 { 1361 char *ep; 1362 1363 ep = p; 1364 do { 1365 ep = strchr(ep, '\n'); 1366 if(ep == nil){ 1367 ep = e; 1368 break; 1369 } 1370 ep++; 1371 if(ep >= e){ 1372 ep = e; 1373 break; 1374 } 1375 } while(*ep == ' ' || *ep == '\t'); 1376 return ep - p; 1377 } 1378 1379 /* rfc2047 non-ascii */ 1380 typedef struct Charset Charset; 1381 struct Charset { 1382 char *name; 1383 int len; 1384 int convert; 1385 char *tcsname; 1386 } charsets[] = 1387 { 1388 { "us-ascii", 8, 1, nil, }, 1389 { "utf-8", 5, 0, nil, }, 1390 { "iso-8859-1", 10, 1, nil, }, 1391 { "iso-8859-2", 10, 2, "8859-2", }, 1392 { "big5", 4, 2, "big5", }, 1393 { "iso-2022-jp", 11, 2, "jis", }, 1394 { "windows-1251", 12, 2, "cp1251"}, 1395 { "koi8-r", 6, 2, "koi8"} 1396 }; 1397 1398 int 1399 rfc2047convert(String *s, char *token, int len) 1400 { 1401 char decoded[1024]; 1402 char utfbuf[2*1024]; 1403 int i; 1404 char *e, *x; 1405 1406 if(len == 0) 1407 return -1; 1408 1409 e = token+len-2; 1410 token += 2; 1411 1412 /* bail if we don't understand the character set */ 1413 for(i = 0; i < nelem(charsets); i++) 1414 if(cistrncmp(charsets[i].name, token, charsets[i].len) == 0) 1415 if(token[charsets[i].len] == '?'){ 1416 token += charsets[i].len + 1; 1417 break; 1418 } 1419 if(i >= nelem(charsets)) 1420 return -1; 1421 1422 /* bail if it doesn't fit */ 1423 if(e-token > sizeof(decoded)-1) 1424 return -1; 1425 1426 /* bail if we don't understand the encoding */ 1427 if(cistrncmp(token, "b?", 2) == 0){ 1428 token += 2; 1429 len = dec64((uchar*)decoded, sizeof(decoded), token, e-token); 1430 decoded[len] = 0; 1431 } else if(cistrncmp(token, "q?", 2) == 0){ 1432 token += 2; 1433 len = decquoted(decoded, token, e); 1434 if(len > 0 && decoded[len-1] == '\n') 1435 len--; 1436 decoded[len] = 0; 1437 } else 1438 return -1; 1439 1440 switch(charsets[i].convert){ 1441 case 0: 1442 s_append(s, decoded); 1443 break; 1444 case 1: 1445 latin1toutf(utfbuf, decoded, decoded+len); 1446 s_append(s, utfbuf); 1447 break; 1448 case 2: 1449 if(xtoutf(charsets[i].tcsname, &x, decoded, decoded+len) <= 0){ 1450 s_append(s, decoded); 1451 } else { 1452 s_append(s, x); 1453 free(x); 1454 } 1455 break; 1456 } 1457 1458 return 0; 1459 } 1460 1461 char* 1462 rfc2047start(char *start, char *end) 1463 { 1464 int quests; 1465 1466 if(*--end != '=') 1467 return nil; 1468 if(*--end != '?') 1469 return nil; 1470 1471 quests = 0; 1472 for(end--; end >= start; end--){ 1473 switch(*end){ 1474 case '=': 1475 if(quests == 3 && *(end+1) == '?') 1476 return end; 1477 break; 1478 case '?': 1479 ++quests; 1480 break; 1481 case ' ': 1482 case '\t': 1483 case '\n': 1484 case '\r': 1485 /* can't have white space in a token */ 1486 return nil; 1487 } 1488 } 1489 return nil; 1490 } 1491 1492 /* convert a header line */ 1493 String* 1494 stringconvert(String *s, char *uneaten, int len) 1495 { 1496 char *token; 1497 char *p; 1498 int i; 1499 1500 s = s_reset(s); 1501 p = uneaten; 1502 for(i = 0; i < len; i++){ 1503 if(*p++ == '='){ 1504 token = rfc2047start(uneaten, p); 1505 if(token != nil){ 1506 s_nappend(s, uneaten, token-uneaten); 1507 if(rfc2047convert(s, token, p - token) < 0) 1508 s_nappend(s, token, p - token); 1509 uneaten = p; 1510 } 1511 } 1512 } 1513 if(p > uneaten) 1514 s_nappend(s, uneaten, p-uneaten); 1515 return s; 1516 } 1517 1518 int 1519 readheader(Message *m, char *buf, int off, int cnt) 1520 { 1521 char *p, *e; 1522 int n, ns; 1523 char *to = buf; 1524 String *s; 1525 1526 p = m->header; 1527 e = m->hend; 1528 s = nil; 1529 1530 /* copy in good headers */ 1531 while(cnt > 0 && p < e){ 1532 n = hdrlen(p, e); 1533 if(ignore(p)){ 1534 p += n; 1535 continue; 1536 } 1537 1538 /* rfc2047 processing */ 1539 s = stringconvert(s, p, n); 1540 ns = s_len(s); 1541 if(off > 0){ 1542 if(ns <= off){ 1543 off -= ns; 1544 p += n; 1545 continue; 1546 } 1547 ns -= off; 1548 } 1549 if(ns > cnt) 1550 ns = cnt; 1551 memmove(to, s_to_c(s)+off, ns); 1552 to += ns; 1553 p += n; 1554 cnt -= ns; 1555 off = 0; 1556 } 1557 1558 s_free(s); 1559 return to - buf; 1560 } 1561 1562 int 1563 headerlen(Message *m) 1564 { 1565 char buf[1024]; 1566 int i, n; 1567 1568 if(m->hlen >= 0) 1569 return m->hlen; 1570 for(n = 0; ; n += i){ 1571 i = readheader(m, buf, n, sizeof(buf)); 1572 if(i <= 0) 1573 break; 1574 } 1575 m->hlen = n; 1576 return n; 1577 } 1578 1579 QLock hashlock; 1580 1581 uint 1582 hash(ulong ppath, char *name) 1583 { 1584 uchar *p; 1585 uint h; 1586 1587 h = 0; 1588 for(p = (uchar*)name; *p; p++) 1589 h = h*7 + *p; 1590 h += ppath; 1591 1592 return h % Hsize; 1593 } 1594 1595 Hash* 1596 hlook(ulong ppath, char *name) 1597 { 1598 int h; 1599 Hash *hp; 1600 1601 qlock(&hashlock); 1602 h = hash(ppath, name); 1603 for(hp = htab[h]; hp != nil; hp = hp->next) 1604 if(ppath == hp->ppath && strcmp(name, hp->name) == 0){ 1605 qunlock(&hashlock); 1606 return hp; 1607 } 1608 qunlock(&hashlock); 1609 return nil; 1610 } 1611 1612 void 1613 henter(ulong ppath, char *name, Qid qid, Message *m, Mailbox *mb) 1614 { 1615 int h; 1616 Hash *hp, **l; 1617 1618 qlock(&hashlock); 1619 h = hash(ppath, name); 1620 for(l = &htab[h]; *l != nil; l = &(*l)->next){ 1621 hp = *l; 1622 if(ppath == hp->ppath && strcmp(name, hp->name) == 0){ 1623 hp->m = m; 1624 hp->mb = mb; 1625 hp->qid = qid; 1626 qunlock(&hashlock); 1627 return; 1628 } 1629 } 1630 1631 *l = hp = emalloc(sizeof(*hp)); 1632 hp->m = m; 1633 hp->mb = mb; 1634 hp->qid = qid; 1635 hp->name = name; 1636 hp->ppath = ppath; 1637 qunlock(&hashlock); 1638 } 1639 1640 void 1641 hfree(ulong ppath, char *name) 1642 { 1643 int h; 1644 Hash *hp, **l; 1645 1646 qlock(&hashlock); 1647 h = hash(ppath, name); 1648 for(l = &htab[h]; *l != nil; l = &(*l)->next){ 1649 hp = *l; 1650 if(ppath == hp->ppath && strcmp(name, hp->name) == 0){ 1651 hp->mb = nil; 1652 *l = hp->next; 1653 free(hp); 1654 break; 1655 } 1656 } 1657 qunlock(&hashlock); 1658 } 1659 1660 int 1661 hashmboxrefs(Mailbox *mb) 1662 { 1663 int h; 1664 Hash *hp; 1665 int refs = 0; 1666 1667 qlock(&hashlock); 1668 for(h = 0; h < Hsize; h++){ 1669 for(hp = htab[h]; hp != nil; hp = hp->next) 1670 if(hp->mb == mb) 1671 refs++; 1672 } 1673 qunlock(&hashlock); 1674 return refs; 1675 } 1676 1677 void 1678 checkmboxrefs(void) 1679 { 1680 int f, refs; 1681 Mailbox *mb; 1682 1683 qlock(&mbllock); 1684 for(mb=mbl; mb; mb=mb->next){ 1685 qlock(&mb->ql); 1686 refs = (f=fidmboxrefs(mb))+1; 1687 if(refs != mb->refs){ 1688 fprint(2, "mbox %s %s ref mismatch actual %d (%d+1) expected %d\n", mb->name, mb->path, refs, f, mb->refs); 1689 abort(); 1690 } 1691 qunlock(&mb->ql); 1692 } 1693 qunlock(&mbllock); 1694 } 1695 1696 void 1697 post(char *name, char *envname, int srvfd) 1698 { 1699 int fd; 1700 char buf[32]; 1701 1702 fd = create(name, OWRITE, 0600); 1703 if(fd < 0) 1704 error("post failed"); 1705 sprint(buf, "%d",srvfd); 1706 if(write(fd, buf, strlen(buf)) != strlen(buf)) 1707 error("srv write"); 1708 close(fd); 1709 putenv(envname, name); 1710 }