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