URI:
       tfs.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tfs.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 }