aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/upas/fs/fs.c
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2005-10-29 16:26:44 +0000
committerrsc <devnull@localhost>2005-10-29 16:26:44 +0000
commit5cdb17983ae6e6367ad7a940cb219eab247a9304 (patch)
tree8ca1ef49af2a96e7daebe624d91fdf679814a057 /src/cmd/upas/fs/fs.c
parentcd3745196389579fb78b9b01ef1daefb5a57aa71 (diff)
downloadplan9port-5cdb17983ae6e6367ad7a940cb219eab247a9304.tar.gz
plan9port-5cdb17983ae6e6367ad7a940cb219eab247a9304.tar.bz2
plan9port-5cdb17983ae6e6367ad7a940cb219eab247a9304.zip
Thanks to John Cummings.
Diffstat (limited to 'src/cmd/upas/fs/fs.c')
-rw-r--r--src/cmd/upas/fs/fs.c1704
1 files changed, 1704 insertions, 0 deletions
diff --git a/src/cmd/upas/fs/fs.c b/src/cmd/upas/fs/fs.c
new file mode 100644
index 00000000..b68b8d2e
--- /dev/null
+++ b/src/cmd/upas/fs/fs.c
@@ -0,0 +1,1704 @@
+#include "common.h"
+#include <auth.h>
+#include <fcall.h>
+#include <libsec.h>
+#include <9pclient.h> /* jpc */
+#include <thread.h> /* jpc */
+#include "dat.h"
+
+enum
+{
+ OPERM = 0x3, // mask of all permission types in open mode
+};
+
+typedef struct Fid Fid;
+
+struct Fid
+{
+ Qid qid;
+ short busy;
+ short open;
+ int fid;
+ Fid *next;
+ Mailbox *mb;
+ Message *m;
+ Message *mtop; // top level message
+
+ //finger pointers to speed up reads of large directories
+ long foff; // offset/DIRLEN of finger
+ Message *fptr; // pointer to message at off
+ int fvers; // mailbox version when finger was saved
+};
+
+ulong path; // incremented for each new file
+Fid *fids;
+int mfd[2];
+char user[Elemlen];
+int messagesize = 4*1024*IOHDRSZ;
+uchar mdata[8*1024*IOHDRSZ];
+uchar mbuf[8*1024*IOHDRSZ];
+Fcall thdr;
+Fcall rhdr;
+int fflg;
+char *mntpt;
+int biffing;
+int plumbing = 1;
+
+QLock mbllock;
+Mailbox *mbl;
+
+Fid *newfid(int);
+void error(char*);
+void io(void);
+void *erealloc(void*, ulong);
+void *emalloc(ulong);
+void usage(void);
+void run_io(void*);
+void reader(void*);
+int readheader(Message*, char*, int, int);
+int cistrncmp(char*, char*, int);
+int tokenconvert(String*, char*, int);
+String* stringconvert(String*, char*, int);
+void post(char*, char*, int);
+
+char *rflush(Fid*), *rauth(Fid*),
+ *rattach(Fid*), *rwalk(Fid*),
+ *ropen(Fid*), *rcreate(Fid*),
+ *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
+ *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),
+ *rversion(Fid*);
+
+char *(*fcalls[])(Fid*) = {
+ [Tflush] rflush,
+ [Tversion] rversion,
+ [Tauth] rauth,
+ [Tattach] rattach,
+ [Twalk] rwalk,
+ [Topen] ropen,
+ [Tcreate] rcreate,
+ [Tread] rread,
+ [Twrite] rwrite,
+ [Tclunk] rclunk,
+ [Tremove] rremove,
+ [Tstat] rstat,
+ [Twstat] rwstat,
+};
+
+char Eperm[] = "permission denied";
+char Enotdir[] = "not a directory";
+char Enoauth[] = "upas/fs: authentication not required";
+char Enotexist[] = "file does not exist";
+char Einuse[] = "file in use";
+char Eexist[] = "file exists";
+char Enotowner[] = "not owner";
+char Eisopen[] = "file already open for I/O";
+char Excl[] = "exclusive use file already open";
+char Ename[] = "illegal name";
+char Ebadctl[] = "unknown control message";
+
+char *dirtab[] =
+{
+[Qdir] ".",
+[Qbody] "body",
+[Qbcc] "bcc",
+[Qcc] "cc",
+[Qdate] "date",
+[Qdigest] "digest",
+[Qdisposition] "disposition",
+[Qfilename] "filename",
+[Qfrom] "from",
+[Qheader] "header",
+[Qinfo] "info",
+[Qinreplyto] "inreplyto",
+[Qlines] "lines",
+[Qmimeheader] "mimeheader",
+[Qmessageid] "messageid",
+[Qraw] "raw",
+[Qrawunix] "rawunix",
+[Qrawbody] "rawbody",
+[Qrawheader] "rawheader",
+[Qreplyto] "replyto",
+[Qsender] "sender",
+[Qsubject] "subject",
+[Qto] "to",
+[Qtype] "type",
+[Qunixdate] "unixdate",
+[Qunixheader] "unixheader",
+[Qctl] "ctl",
+[Qmboxctl] "ctl",
+};
+
+enum
+{
+ Hsize= 1277,
+};
+
+Hash *htab[Hsize];
+
+int debug;
+int fflag;
+int logging;
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-b -m mountpoint]\n", argv0);
+ threadexits("usage");
+}
+
+void
+notifyf(void *a, char *s)
+{
+ USED(a);
+ if(strncmp(s, "interrupt", 9) == 0)
+ noted(NCONT);
+ noted(NDFLT);
+}
+
+void
+threadmain(int argc, char *argv[])
+{
+ int p[2], std, nodflt;
+ char maildir[128];
+ char mbox[128];
+ char *mboxfile, *err;
+ char srvfile[64];
+ int srvpost;
+
+ rfork(RFNOTEG);
+ mntpt = nil;
+ fflag = 0;
+ mboxfile = nil;
+ std = 0;
+ nodflt = 0;
+ srvpost = 0;
+
+ ARGBEGIN{
+ case 'b':
+ biffing = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ mboxfile = ARGF();
+ break;
+ case 'm':
+ mntpt = ARGF();
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'p':
+ plumbing = 0;
+ break;
+ case 's':
+ srvpost = 1;
+ break;
+ case 'l':
+ logging = 1;
+ break;
+ case 'n':
+ nodflt = 1;
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(pipe(p) < 0)
+ error("pipe failed");
+ mfd[0] = p[0];
+ mfd[1] = p[0];
+
+ notify(notifyf);
+ strcpy(user, getuser());
+ if(mntpt == nil){
+ snprint(maildir, sizeof(maildir), "/mail/fs");
+ mntpt = maildir;
+ }
+ if(mboxfile == nil && !nodflt){
+ snprint(mbox, sizeof(mbox), "/mail/box/%s/mbox", user);
+ mboxfile = mbox;
+ std = 1;
+ }
+
+ if(debug)
+ fmtinstall('F', fcallfmt);
+
+ if(mboxfile != nil){
+ err = newmbox(mboxfile, "mbox", std);
+ if(err != nil)
+ sysfatal("opening mailbox: %s", err);
+ }
+
+ switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){ /* jpc removed RFEND */
+ case -1:
+ error("fork");
+ case 0:
+ henter(PATH(0, Qtop), dirtab[Qctl],
+ (Qid){PATH(0, Qctl), 0, QTFILE}, nil, nil);
+ close(p[1]);
+ io();
+ postnote(PNGROUP, getpid(), "die yankee pig dog");
+ break;
+ default:
+ close(p[0]); /* don't deadlock if child fails */
+ if(srvpost){
+ sprint(srvfile, "/srv/upasfs.%s", user);
+ /* post(srvfile, "upasfs", p[1]); jpc */
+ post9pservice(p[1], "upasfs"); /* jpc */
+ } else {
+ error("tried to mount, fixme"); /* jpc */
+ /* if(mount(p[1], -1, mntpt, MREPL, "") < 0)
+ error("mount failed"); jpc */
+ }
+ }
+ threadexits(0);
+}
+
+void run_io(void *v) {
+ int *p;
+
+ p = v;
+ henter(PATH(0, Qtop), dirtab[Qctl],
+ (Qid){PATH(0, Qctl), 0, QTFILE}, nil, nil);
+ close(p[1]);
+ io();
+ postnote(PNGROUP, getpid(), "die yankee pig dog");
+}
+
+static int
+fileinfo(Message *m, int t, char **pp)
+{
+ char *p;
+ int len;
+
+ p = "";
+ len = 0;
+ switch(t){
+ case Qbody:
+ p = m->body;
+ len = m->bend - m->body;
+ break;
+ case Qbcc:
+ if(m->bcc822){
+ p = s_to_c(m->bcc822);
+ len = strlen(p);
+ }
+ break;
+ case Qcc:
+ if(m->cc822){
+ p = s_to_c(m->cc822);
+ len = strlen(p);
+ }
+ break;
+ case Qdisposition:
+ switch(m->disposition){
+ case Dinline:
+ p = "inline";
+ break;
+ case Dfile:
+ p = "file";
+ break;
+ }
+ len = strlen(p);
+ break;
+ case Qdate:
+ if(m->date822){
+ p = s_to_c(m->date822);
+ len = strlen(p);
+ } else if(m->unixdate != nil){
+ p = s_to_c(m->unixdate);
+ len = strlen(p);
+ }
+ break;
+ case Qfilename:
+ if(m->filename){
+ p = s_to_c(m->filename);
+ len = strlen(p);
+ }
+ break;
+ case Qinreplyto:
+ if(m->inreplyto822){
+ p = s_to_c(m->inreplyto822);
+ len = strlen(p);
+ }
+ break;
+ case Qmessageid:
+ if(m->messageid822){
+ p = s_to_c(m->messageid822);
+ len = strlen(p);
+ }
+ break;
+ case Qfrom:
+ if(m->from822){
+ p = s_to_c(m->from822);
+ len = strlen(p);
+ } else if(m->unixfrom != nil){
+ p = s_to_c(m->unixfrom);
+ len = strlen(p);
+ }
+ break;
+ case Qheader:
+ p = m->header;
+ len = headerlen(m);
+ break;
+ case Qlines:
+ p = m->lines;
+ if(*p == 0)
+ countlines(m);
+ len = strlen(m->lines);
+ break;
+ case Qraw:
+ p = m->start;
+ if(strncmp(m->start, "From ", 5) == 0){
+ p = strchr(p, '\n');
+ if(p == nil)
+ p = m->start;
+ else
+ p++;
+ }
+ len = m->end - p;
+ break;
+ case Qrawunix:
+ p = m->start;
+ len = m->end - p;
+ break;
+ case Qrawbody:
+ p = m->rbody;
+ len = m->rbend - p;
+ break;
+ case Qrawheader:
+ p = m->header;
+ len = m->hend - p;
+ break;
+ case Qmimeheader:
+ p = m->mheader;
+ len = m->mhend - p;
+ break;
+ case Qreplyto:
+ p = nil;
+ if(m->replyto822 != nil){
+ p = s_to_c(m->replyto822);
+ len = strlen(p);
+ } else if(m->from822 != nil){
+ p = s_to_c(m->from822);
+ len = strlen(p);
+ } else if(m->sender822 != nil){
+ p = s_to_c(m->sender822);
+ len = strlen(p);
+ } else if(m->unixfrom != nil){
+ p = s_to_c(m->unixfrom);
+ len = strlen(p);
+ }
+ break;
+ case Qsender:
+ if(m->sender822){
+ p = s_to_c(m->sender822);
+ len = strlen(p);
+ }
+ break;
+ case Qsubject:
+ p = nil;
+ if(m->subject822){
+ p = s_to_c(m->subject822);
+ len = strlen(p);
+ }
+ break;
+ case Qto:
+ if(m->to822){
+ p = s_to_c(m->to822);
+ len = strlen(p);
+ }
+ break;
+ case Qtype:
+ if(m->type){
+ p = s_to_c(m->type);
+ len = strlen(p);
+ }
+ break;
+ case Qunixdate:
+ if(m->unixdate){
+ p = s_to_c(m->unixdate);
+ len = strlen(p);
+ }
+ break;
+ case Qunixheader:
+ if(m->unixheader){
+ p = s_to_c(m->unixheader);
+ len = s_len(m->unixheader);
+ }
+ break;
+ case Qdigest:
+ if(m->sdigest){
+ p = s_to_c(m->sdigest);
+ len = strlen(p);
+ }
+ break;
+ }
+ *pp = p;
+ return len;
+}
+
+int infofields[] = {
+ Qfrom,
+ Qto,
+ Qcc,
+ Qreplyto,
+ Qunixdate,
+ Qsubject,
+ Qtype,
+ Qdisposition,
+ Qfilename,
+ Qdigest,
+ Qbcc,
+ Qinreplyto,
+ Qdate,
+ Qsender,
+ Qmessageid,
+ Qlines,
+ -1,
+};
+
+static int
+readinfo(Message *m, char *buf, long off, int count)
+{
+ char *p;
+ int len, i, n;
+ String *s;
+
+ s = s_new();
+ len = 0;
+ for(i = 0; len < count && infofields[i] >= 0; i++){
+ n = fileinfo(m, infofields[i], &p);
+ s = stringconvert(s, p, n);
+ s_append(s, "\n");
+ p = s_to_c(s);
+ n = strlen(p);
+ if(off > 0){
+ if(off >= n){
+ off -= n;
+ continue;
+ }
+ p += off;
+ n -= off;
+ off = 0;
+ }
+ if(n > count - len)
+ n = count - len;
+ if(buf)
+ memmove(buf+len, p, n);
+ len += n;
+ }
+ s_free(s);
+ return len;
+}
+
+static void
+mkstat(Dir *d, Mailbox *mb, Message *m, int t)
+{
+ char *p;
+
+ d->uid = user;
+ d->gid = user;
+ d->muid = user;
+ d->mode = 0444;
+ d->qid.vers = 0;
+ d->qid.type = QTFILE;
+ d->type = 0;
+ d->dev = 0;
+ if(mb != nil && mb->d != nil){
+ d->atime = mb->d->atime;
+ d->mtime = mb->d->mtime;
+ } else {
+ d->atime = time(0);
+ d->mtime = d->atime;
+ }
+
+ switch(t){
+ case Qtop:
+ d->name = ".";
+ d->mode = DMDIR|0555;
+ d->atime = d->mtime = time(0);
+ d->length = 0;
+ d->qid.path = PATH(0, Qtop);
+ d->qid.type = QTDIR;
+ break;
+ case Qmbox:
+ d->name = mb->name;
+ d->mode = DMDIR|0555;
+ d->length = 0;
+ d->qid.path = PATH(mb->id, Qmbox);
+ d->qid.type = QTDIR;
+ d->qid.vers = mb->vers;
+ break;
+ case Qdir:
+ d->name = m->name;
+ d->mode = DMDIR|0555;
+ d->length = 0;
+ d->qid.path = PATH(m->id, Qdir);
+ d->qid.type = QTDIR;
+ break;
+ case Qctl:
+ d->name = dirtab[t];
+ d->mode = 0666;
+ d->atime = d->mtime = time(0);
+ d->length = 0;
+ d->qid.path = PATH(0, Qctl);
+ break;
+ case Qmboxctl:
+ d->name = dirtab[t];
+ d->mode = 0222;
+ d->atime = d->mtime = time(0);
+ d->length = 0;
+ d->qid.path = PATH(mb->id, Qmboxctl);
+ break;
+ case Qinfo:
+ d->name = dirtab[t];
+ d->length = readinfo(m, nil, 0, 1<<30);
+ d->qid.path = PATH(m->id, t);
+ break;
+ default:
+ d->name = dirtab[t];
+ d->length = fileinfo(m, t, &p);
+ d->qid.path = PATH(m->id, t);
+ break;
+ }
+}
+
+char*
+rversion(Fid* dummy)
+{
+ Fid *f;
+
+ if(thdr.msize < 256)
+ return "max messagesize too small";
+ if(thdr.msize < messagesize)
+ messagesize = thdr.msize;
+ rhdr.msize = messagesize;
+ if(strncmp(thdr.version, "9P2000", 6) != 0)
+ return "unknown 9P version";
+ else
+ rhdr.version = "9P2000";
+ for(f = fids; f; f = f->next)
+ if(f->busy)
+ rclunk(f);
+ return nil;
+}
+
+char*
+rauth(Fid* dummy)
+{
+ return Enoauth;
+}
+
+char*
+rflush(Fid *f)
+{
+ USED(f);
+ return 0;
+}
+
+char*
+rattach(Fid *f)
+{
+ f->busy = 1;
+ f->m = nil;
+ f->mb = nil;
+ f->qid.path = PATH(0, Qtop);
+ f->qid.type = QTDIR;
+ f->qid.vers = 0;
+ rhdr.qid = f->qid;
+ if(strcmp(thdr.uname, user) != 0)
+ return Eperm;
+ return 0;
+}
+
+static Fid*
+doclone(Fid *f, int nfid)
+{
+ Fid *nf;
+
+ nf = newfid(nfid);
+ if(nf->busy)
+ return nil;
+ nf->busy = 1;
+ nf->open = 0;
+ nf->m = f->m;
+ nf->mtop = f->mtop;
+ nf->mb = f->mb;
+ if(f->mb != nil)
+ mboxincref(f->mb);
+ if(f->mtop != nil){
+ qlock(&f->mb->ql);
+ msgincref(f->mtop);
+ qunlock(&f->mb->ql);
+ }
+ nf->qid = f->qid;
+ return nf;
+}
+
+char*
+dowalk(Fid *f, char *name)
+{
+ int t;
+ Mailbox *omb, *mb;
+ char *rv, *p;
+ Hash *h;
+
+ t = FILE(f->qid.path);
+
+ rv = Enotexist;
+
+ omb = f->mb;
+ if(omb)
+ qlock(&omb->ql);
+ else
+ qlock(&mbllock);
+
+ // this must catch everything except . and ..
+retry:
+ h = hlook(f->qid.path, name);
+ if(h != nil){
+ f->mb = h->mb;
+ f->m = h->m;
+ switch(t){
+ case Qtop:
+ if(f->mb != nil)
+ mboxincref(f->mb);
+ break;
+ case Qmbox:
+ if(f->m){
+ msgincref(f->m);
+ f->mtop = f->m;
+ }
+ break;
+ }
+ f->qid = h->qid;
+ rv = nil;
+ } else if((p = strchr(name, '.')) != nil && *name != '.'){
+ *p = 0;
+ goto retry;
+ }
+
+ if(omb)
+ qunlock(&omb->ql);
+ else
+ qunlock(&mbllock);
+ if(rv == nil)
+ return rv;
+
+ if(strcmp(name, ".") == 0)
+ return nil;
+
+ if(f->qid.type != QTDIR)
+ return Enotdir;
+
+ if(strcmp(name, "..") == 0){
+ switch(t){
+ case Qtop:
+ f->qid.path = PATH(0, Qtop);
+ f->qid.type = QTDIR;
+ f->qid.vers = 0;
+ break;
+ case Qmbox:
+ f->qid.path = PATH(0, Qtop);
+ f->qid.type = QTDIR;
+ f->qid.vers = 0;
+ qlock(&mbllock);
+ mb = f->mb;
+ f->mb = nil;
+ mboxdecref(mb);
+ qunlock(&mbllock);
+ break;
+ case Qdir:
+ qlock(&f->mb->ql);
+ if(f->m->whole == f->mb->root){
+ f->qid.path = PATH(f->mb->id, Qmbox);
+ f->qid.type = QTDIR;
+ f->qid.vers = f->mb->d->qid.vers;
+ msgdecref(f->mb, f->mtop);
+ f->m = f->mtop = nil;
+ } else {
+ f->m = f->m->whole;
+ f->qid.path = PATH(f->m->id, Qdir);
+ f->qid.type = QTDIR;
+ }
+ qunlock(&f->mb->ql);
+ break;
+ }
+ rv = nil;
+ }
+ return rv;
+}
+
+char*
+rwalk(Fid *f)
+{
+ Fid *nf;
+ char *rv;
+ int i;
+
+ if(f->open)
+ return Eisopen;
+
+ rhdr.nwqid = 0;
+ nf = nil;
+
+ /* clone if requested */
+ if(thdr.newfid != thdr.fid){
+ nf = doclone(f, thdr.newfid);
+ if(nf == nil)
+ return "new fid in use";
+ f = nf;
+ }
+
+ /* if it's just a clone, return */
+ if(thdr.nwname == 0 && nf != nil)
+ return nil;
+
+ /* walk each element */
+ rv = nil;
+ for(i = 0; i < thdr.nwname; i++){
+ rv = dowalk(f, thdr.wname[i]);
+ if(rv != nil){
+ if(nf != nil)
+ rclunk(nf);
+ break;
+ }
+ rhdr.wqid[i] = f->qid;
+ }
+ rhdr.nwqid = i;
+
+ /* we only error out if no walk */
+ if(i > 0)
+ rv = nil;
+
+ return rv;
+}
+
+char *
+ropen(Fid *f)
+{
+ int file;
+
+ if(f->open)
+ return Eisopen;
+
+ file = FILE(f->qid.path);
+ if(thdr.mode != OREAD)
+ if(file != Qctl && file != Qmboxctl)
+ return Eperm;
+
+ // make sure we've decoded
+ if(file == Qbody){
+ if(f->m->decoded == 0)
+ decode(f->m);
+ if(f->m->converted == 0)
+ convert(f->m);
+ }
+
+ rhdr.iounit = 0;
+ rhdr.qid = f->qid;
+ f->open = 1;
+ return 0;
+}
+
+char *
+rcreate(Fid* dummy)
+{
+ return Eperm;
+}
+
+int
+readtopdir(Fid* dummy, uchar *buf, long off, int cnt, int blen)
+{
+ Dir d;
+ int m, n;
+ long pos;
+ Mailbox *mb;
+
+ n = 0;
+ pos = 0;
+ mkstat(&d, nil, nil, Qctl);
+ m = convD2M(&d, &buf[n], blen);
+ if(off <= pos){
+ if(m <= BIT16SZ || m > cnt)
+ return 0;
+ n += m;
+ cnt -= m;
+ }
+ pos += m;
+
+ for(mb = mbl; mb != nil; mb = mb->next){
+ mkstat(&d, mb, nil, Qmbox);
+ m = convD2M(&d, &buf[n], blen-n);
+ if(off <= pos){
+ if(m <= BIT16SZ || m > cnt)
+ break;
+ n += m;
+ cnt -= m;
+ }
+ pos += m;
+ }
+ return n;
+}
+
+int
+readmboxdir(Fid *f, uchar *buf, long off, int cnt, int blen)
+{
+ Dir d;
+ int n, m;
+ long pos;
+ Message *msg;
+
+ n = 0;
+ if(f->mb->ctl){
+ mkstat(&d, f->mb, nil, Qmboxctl);
+ m = convD2M(&d, &buf[n], blen);
+ if(off == 0){
+ if(m <= BIT16SZ || m > cnt){
+ f->fptr = nil;
+ return 0;
+ }
+ n += m;
+ cnt -= m;
+ } else
+ off -= m;
+ }
+
+ // to avoid n**2 reads of the directory, use a saved finger pointer
+ if(f->mb->vers == f->fvers && off >= f->foff && f->fptr != nil){
+ msg = f->fptr;
+ pos = f->foff;
+ } else {
+ msg = f->mb->root->part;
+ pos = 0;
+ }
+
+ for(; cnt > 0 && msg != nil; msg = msg->next){
+ // act like deleted files aren't there
+ if(msg->deleted)
+ continue;
+
+ mkstat(&d, f->mb, msg, Qdir);
+ m = convD2M(&d, &buf[n], blen-n);
+ if(off <= pos){
+ if(m <= BIT16SZ || m > cnt)
+ break;
+ n += m;
+ cnt -= m;
+ }
+ pos += m;
+ }
+
+ // save a finger pointer for next read of the mbox directory
+ f->foff = pos;
+ f->fptr = msg;
+ f->fvers = f->mb->vers;
+
+ return n;
+}
+
+int
+readmsgdir(Fid *f, uchar *buf, long off, int cnt, int blen)
+{
+ Dir d;
+ int i, n, m;
+ long pos;
+ Message *msg;
+
+ n = 0;
+ pos = 0;
+ for(i = 0; i < Qmax; i++){
+ mkstat(&d, f->mb, f->m, i);
+ m = convD2M(&d, &buf[n], blen-n);
+ if(off <= pos){
+ if(m <= BIT16SZ || m > cnt)
+ return n;
+ n += m;
+ cnt -= m;
+ }
+ pos += m;
+ }
+ for(msg = f->m->part; msg != nil; msg = msg->next){
+ mkstat(&d, f->mb, msg, Qdir);
+ m = convD2M(&d, &buf[n], blen-n);
+ if(off <= pos){
+ if(m <= BIT16SZ || m > cnt)
+ break;
+ n += m;
+ cnt -= m;
+ }
+ pos += m;
+ }
+
+ return n;
+}
+
+char*
+rread(Fid *f)
+{
+ long off;
+ int t, i, n, cnt;
+ char *p;
+
+ rhdr.count = 0;
+ off = thdr.offset;
+ cnt = thdr.count;
+
+ if(cnt > messagesize - IOHDRSZ)
+ cnt = messagesize - IOHDRSZ;
+
+ rhdr.data = (char*)mbuf;
+
+ t = FILE(f->qid.path);
+ if(f->qid.type & QTDIR){
+ if(t == Qtop) {
+ qlock(&mbllock);
+ n = readtopdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
+ qunlock(&mbllock);
+ } else if(t == Qmbox) {
+ qlock(&f->mb->ql);
+ if(off == 0)
+ syncmbox(f->mb, 1);
+ n = readmboxdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
+ qunlock(&f->mb->ql);
+ } else if(t == Qmboxctl) {
+ n = 0;
+ } else {
+ n = readmsgdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
+ }
+
+ rhdr.count = n;
+ return nil;
+ }
+
+ if(FILE(f->qid.path) == Qheader){
+ rhdr.count = readheader(f->m, (char*)mbuf, off, cnt);
+ return nil;
+ }
+
+ if(FILE(f->qid.path) == Qinfo){
+ rhdr.count = readinfo(f->m, (char*)mbuf, off, cnt);
+ return nil;
+ }
+
+ i = fileinfo(f->m, FILE(f->qid.path), &p);
+ if(off < i){
+ if((off + cnt) > i)
+ cnt = i - off;
+ memmove(mbuf, p + off, cnt);
+ rhdr.count = cnt;
+ }
+ return nil;
+}
+
+char*
+rwrite(Fid *f)
+{
+ char *err;
+ char *token[1024];
+ int t, n;
+ String *file;
+
+ t = FILE(f->qid.path);
+ rhdr.count = thdr.count;
+ switch(t){
+ case Qctl:
+ if(thdr.count == 0)
+ return Ebadctl;
+ if(thdr.data[thdr.count-1] == '\n')
+ thdr.data[thdr.count-1] = 0;
+ else
+ thdr.data[thdr.count] = 0;
+ n = tokenize(thdr.data, token, nelem(token));
+ if(n == 0)
+ return Ebadctl;
+ if(strcmp(token[0], "open") == 0){
+ file = s_new();
+ switch(n){
+ case 1:
+ err = Ebadctl;
+ break;
+ case 2:
+ mboxpath(token[1], getlog(), file, 0);
+ err = newmbox(s_to_c(file), nil, 0);
+ break;
+ default:
+ mboxpath(token[1], getlog(), file, 0);
+ if(strchr(token[2], '/') != nil)
+ err = "/ not allowed in mailbox name";
+ else
+ err = newmbox(s_to_c(file), token[2], 0);
+ break;
+ }
+ s_free(file);
+ return err;
+ }
+ if(strcmp(token[0], "close") == 0){
+ if(n < 2)
+ return nil;
+ freembox(token[1]);
+ return nil;
+ }
+ if(strcmp(token[0], "delete") == 0){
+ if(n < 3)
+ return nil;
+ delmessages(n-1, &token[1]);
+ return nil;
+ }
+ return Ebadctl;
+ case Qmboxctl:
+ if(f->mb && f->mb->ctl){
+ if(thdr.count == 0)
+ return Ebadctl;
+ if(thdr.data[thdr.count-1] == '\n')
+ thdr.data[thdr.count-1] = 0;
+ else
+ thdr.data[thdr.count] = 0;
+ n = tokenize(thdr.data, token, nelem(token));
+ if(n == 0)
+ return Ebadctl;
+ return (*f->mb->ctl)(f->mb, n, token);
+ }
+ }
+ return Eperm;
+}
+
+char *
+rclunk(Fid *f)
+{
+ Mailbox *mb;
+
+ f->busy = 0;
+ f->open = 0;
+ if(f->mtop != nil){
+ qlock(&f->mb->ql);
+ msgdecref(f->mb, f->mtop);
+ qunlock(&f->mb->ql);
+ }
+ f->m = f->mtop = nil;
+ mb = f->mb;
+ if(mb != nil){
+ f->mb = nil;
+ assert(mb->refs > 0);
+ qlock(&mbllock);
+ mboxdecref(mb);
+ qunlock(&mbllock);
+ }
+ f->fid = -1;
+ return 0;
+}
+
+char *
+rremove(Fid *f)
+{
+ if(f->m != nil){
+ if(f->m->deleted == 0)
+ mailplumb(f->mb, f->m, 1);
+ f->m->deleted = 1;
+ }
+ return rclunk(f);
+}
+
+char *
+rstat(Fid *f)
+{
+ Dir d;
+
+ if(FILE(f->qid.path) == Qmbox){
+ qlock(&f->mb->ql);
+ syncmbox(f->mb, 1);
+ qunlock(&f->mb->ql);
+ }
+ mkstat(&d, f->mb, f->m, FILE(f->qid.path));
+ rhdr.nstat = convD2M(&d, mbuf, messagesize - IOHDRSZ);
+ rhdr.stat = mbuf;
+ return 0;
+}
+
+char *
+rwstat(Fid* dummy)
+{
+ return Eperm;
+}
+
+Fid *
+newfid(int fid)
+{
+ Fid *f, *ff;
+
+ ff = 0;
+ for(f = fids; f; f = f->next)
+ if(f->fid == fid)
+ return f;
+ else if(!ff && !f->busy)
+ ff = f;
+ if(ff){
+ ff->fid = fid;
+ ff->fptr = nil;
+ return ff;
+ }
+ f = emalloc(sizeof *f);
+ f->fid = fid;
+ f->fptr = nil;
+ f->next = fids;
+ fids = f;
+ return f;
+}
+
+int
+fidmboxrefs(Mailbox *mb)
+{
+ Fid *f;
+ int refs = 0;
+
+ for(f = fids; f; f = f->next){
+ if(f->mb == mb)
+ refs++;
+ }
+ return refs;
+}
+
+void
+io(void)
+{
+ char *err;
+ int n, nw;
+
+ /* start a process to watch the mailboxes*/
+ if(plumbing){
+ proccreate(reader, nil, 16000);
+#if 0 /* jpc */
+ switch(rfork(RFPROC|RFMEM)){
+ case -1:
+ /* oh well */
+ break;
+ case 0:
+ reader();
+ threadexits(nil);
+ default:
+ break;
+ }
+#endif /* jpc */
+ }
+
+ for(;;){
+ /*
+ * reading from a pipe or a network device
+ * will give an error after a few eof reads
+ * however, we cannot tell the difference
+ * between a zero-length read and an interrupt
+ * on the processes writing to us,
+ * so we wait for the error
+ */
+ checkmboxrefs();
+ n = read9pmsg(mfd[0], mdata, messagesize);
+ if(n == 0)
+ continue;
+ if(n < 0)
+ return;
+ if(convM2S(mdata, n, &thdr) == 0)
+ continue;
+
+ if(debug)
+ fprint(2, "%s:<-%F\n", argv0, &thdr);
+
+ rhdr.data = (char*)mdata + messagesize;
+ if(!fcalls[thdr.type])
+ err = "bad fcall type";
+ else
+ err = (*fcalls[thdr.type])(newfid(thdr.fid));
+ if(err){
+ rhdr.type = Rerror;
+ rhdr.ename = err;
+ }else{
+ rhdr.type = thdr.type + 1;
+ rhdr.fid = thdr.fid;
+ }
+ rhdr.tag = thdr.tag;
+ if(debug)
+ fprint(2, "%s:->%F\n", argv0, &rhdr);/**/
+ n = convS2M(&rhdr, mdata, messagesize);
+ if((nw = write(mfd[1], mdata, n)) != n) {
+ fprint(2,"wrote %d bytes\n",nw);
+ error("mount write");
+ }
+ }
+}
+
+void
+reader(void *dummy)
+{
+ ulong t;
+ Dir *d;
+ Mailbox *mb;
+
+ sleep(15*1000);
+ for(;;){
+ t = time(0);
+ qlock(&mbllock);
+ for(mb = mbl; mb != nil; mb = mb->next){
+ assert(mb->refs > 0);
+ if(mb->waketime != 0 && t > mb->waketime){
+ qlock(&mb->ql);
+ mb->waketime = 0;
+ break;
+ }
+
+ d = dirstat(mb->path);
+ if(d == nil)
+ continue;
+
+ qlock(&mb->ql);
+ if(mb->d)
+ if(d->qid.path != mb->d->qid.path
+ || d->qid.vers != mb->d->qid.vers){
+ free(d);
+ break;
+ }
+ qunlock(&mb->ql);
+ free(d);
+ }
+ qunlock(&mbllock);
+ if(mb != nil){
+ syncmbox(mb, 1);
+ qunlock(&mb->ql);
+ } else
+ sleep(15*1000);
+ }
+}
+
+int
+newid(void)
+{
+ int rv;
+ static int id;
+ static Lock idlock;
+
+ lock(&idlock);
+ rv = ++id;
+ unlock(&idlock);
+
+ return rv;
+}
+
+void
+error(char *s)
+{
+ postnote(PNGROUP, getpid(), "die yankee pig dog");
+ fprint(2, "%s: %s: %r\n", argv0, s);
+ threadexits(s);
+}
+
+
+typedef struct Ignorance Ignorance;
+struct Ignorance
+{
+ Ignorance *next;
+ char *str; /* string */
+ int partial; /* true if not exact match */
+};
+Ignorance *ignorance;
+
+/*
+ * read the file of headers to ignore
+ */
+void
+readignore(void)
+{
+ char *p;
+ Ignorance *i;
+ Biobuf *b;
+
+ if(ignorance != nil)
+ return;
+
+ b = Bopen("/mail/lib/ignore", OREAD);
+ if(b == 0)
+ return;
+ while(p = Brdline(b, '\n')){
+ p[Blinelen(b)-1] = 0;
+ while(*p && (*p == ' ' || *p == '\t'))
+ p++;
+ if(*p == '#')
+ continue;
+ i = malloc(sizeof(Ignorance));
+ if(i == 0)
+ break;
+ i->partial = strlen(p);
+ i->str = strdup(p);
+ if(i->str == 0){
+ free(i);
+ break;
+ }
+ i->next = ignorance;
+ ignorance = i;
+ }
+ Bterm(b);
+}
+
+int
+ignore(char *p)
+{
+ Ignorance *i;
+
+ readignore();
+ for(i = ignorance; i != nil; i = i->next)
+ if(cistrncmp(i->str, p, i->partial) == 0)
+ return 1;
+ return 0;
+}
+
+int
+hdrlen(char *p, char *e)
+{
+ char *ep;
+
+ ep = p;
+ do {
+ ep = strchr(ep, '\n');
+ if(ep == nil){
+ ep = e;
+ break;
+ }
+ ep++;
+ if(ep >= e){
+ ep = e;
+ break;
+ }
+ } while(*ep == ' ' || *ep == '\t');
+ return ep - p;
+}
+
+// rfc2047 non-ascii
+typedef struct Charset Charset;
+struct Charset {
+ char *name;
+ int len;
+ int convert;
+ char *tcsname;
+} charsets[] =
+{
+ { "us-ascii", 8, 1, nil, },
+ { "utf-8", 5, 0, nil, },
+ { "iso-8859-1", 10, 1, nil, },
+ { "iso-8859-2", 10, 2, "8859-2", },
+ { "big5", 4, 2, "big5", },
+ { "iso-2022-jp", 11, 2, "jis", },
+ { "windows-1251", 12, 2, "cp1251"},
+ { "koi8-r", 6, 2, "koi8"},
+};
+
+int
+rfc2047convert(String *s, char *token, int len)
+{
+ char decoded[1024];
+ char utfbuf[2*1024];
+ int i;
+ char *e, *x;
+
+ if(len == 0)
+ return -1;
+
+ e = token+len-2;
+ token += 2;
+
+ // bail if we don't understand the character set
+ for(i = 0; i < nelem(charsets); i++)
+ if(cistrncmp(charsets[i].name, token, charsets[i].len) == 0)
+ if(token[charsets[i].len] == '?'){
+ token += charsets[i].len + 1;
+ break;
+ }
+ if(i >= nelem(charsets))
+ return -1;
+
+ // bail if it doesn't fit
+ if(e-token > sizeof(decoded)-1)
+ return -1;
+
+ // bail if we don't understand the encoding
+ if(cistrncmp(token, "b?", 2) == 0){
+ token += 2;
+ len = dec64((uchar*)decoded, sizeof(decoded), token, e-token);
+ decoded[len] = 0;
+ } else if(cistrncmp(token, "q?", 2) == 0){
+ token += 2;
+ len = decquoted(decoded, token, e);
+ if(len > 0 && decoded[len-1] == '\n')
+ len--;
+ decoded[len] = 0;
+ } else
+ return -1;
+
+ switch(charsets[i].convert){
+ case 0:
+ s_append(s, decoded);
+ break;
+ case 1:
+ latin1toutf(utfbuf, decoded, decoded+len);
+ s_append(s, utfbuf);
+ break;
+ case 2:
+ if(xtoutf(charsets[i].tcsname, &x, decoded, decoded+len) <= 0){
+ s_append(s, decoded);
+ } else {
+ s_append(s, x);
+ free(x);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+char*
+rfc2047start(char *start, char *end)
+{
+ int quests;
+
+ if(*--end != '=')
+ return nil;
+ if(*--end != '?')
+ return nil;
+
+ quests = 0;
+ for(end--; end >= start; end--){
+ switch(*end){
+ case '=':
+ if(quests == 3 && *(end+1) == '?')
+ return end;
+ break;
+ case '?':
+ ++quests;
+ break;
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ /* can't have white space in a token */
+ return nil;
+ }
+ }
+ return nil;
+}
+
+// convert a header line
+String*
+stringconvert(String *s, char *uneaten, int len)
+{
+ char *token;
+ char *p;
+ int i;
+
+ s = s_reset(s);
+ p = uneaten;
+ for(i = 0; i < len; i++){
+ if(*p++ == '='){
+ token = rfc2047start(uneaten, p);
+ if(token != nil){
+ s_nappend(s, uneaten, token-uneaten);
+ if(rfc2047convert(s, token, p - token) < 0)
+ s_nappend(s, token, p - token);
+ uneaten = p;
+ }
+ }
+ }
+ if(p > uneaten)
+ s_nappend(s, uneaten, p-uneaten);
+ return s;
+}
+
+int
+readheader(Message *m, char *buf, int off, int cnt)
+{
+ char *p, *e;
+ int n, ns;
+ char *to = buf;
+ String *s;
+
+ p = m->header;
+ e = m->hend;
+ s = nil;
+
+ // copy in good headers
+ while(cnt > 0 && p < e){
+ n = hdrlen(p, e);
+ if(ignore(p)){
+ p += n;
+ continue;
+ }
+
+ // rfc2047 processing
+ s = stringconvert(s, p, n);
+ ns = s_len(s);
+ if(off > 0){
+ if(ns <= off){
+ off -= ns;
+ p += n;
+ continue;
+ }
+ ns -= off;
+ }
+ if(ns > cnt)
+ ns = cnt;
+ memmove(to, s_to_c(s)+off, ns);
+ to += ns;
+ p += n;
+ cnt -= ns;
+ off = 0;
+ }
+
+ s_free(s);
+ return to - buf;
+}
+
+int
+headerlen(Message *m)
+{
+ char buf[1024];
+ int i, n;
+
+ if(m->hlen >= 0)
+ return m->hlen;
+ for(n = 0; ; n += i){
+ i = readheader(m, buf, n, sizeof(buf));
+ if(i <= 0)
+ break;
+ }
+ m->hlen = n;
+ return n;
+}
+
+QLock hashlock;
+
+uint
+hash(ulong ppath, char *name)
+{
+ uchar *p;
+ uint h;
+
+ h = 0;
+ for(p = (uchar*)name; *p; p++)
+ h = h*7 + *p;
+ h += ppath;
+
+ return h % Hsize;
+}
+
+Hash*
+hlook(ulong ppath, char *name)
+{
+ int h;
+ Hash *hp;
+
+ qlock(&hashlock);
+ h = hash(ppath, name);
+ for(hp = htab[h]; hp != nil; hp = hp->next)
+ if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
+ qunlock(&hashlock);
+ return hp;
+ }
+ qunlock(&hashlock);
+ return nil;
+}
+
+void
+henter(ulong ppath, char *name, Qid qid, Message *m, Mailbox *mb)
+{
+ int h;
+ Hash *hp, **l;
+
+ qlock(&hashlock);
+ h = hash(ppath, name);
+ for(l = &htab[h]; *l != nil; l = &(*l)->next){
+ hp = *l;
+ if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
+ hp->m = m;
+ hp->mb = mb;
+ hp->qid = qid;
+ qunlock(&hashlock);
+ return;
+ }
+ }
+
+ *l = hp = emalloc(sizeof(*hp));
+ hp->m = m;
+ hp->mb = mb;
+ hp->qid = qid;
+ hp->name = name;
+ hp->ppath = ppath;
+ qunlock(&hashlock);
+}
+
+void
+hfree(ulong ppath, char *name)
+{
+ int h;
+ Hash *hp, **l;
+
+ qlock(&hashlock);
+ h = hash(ppath, name);
+ for(l = &htab[h]; *l != nil; l = &(*l)->next){
+ hp = *l;
+ if(ppath == hp->ppath && strcmp(name, hp->name) == 0){
+ hp->mb = nil;
+ *l = hp->next;
+ free(hp);
+ break;
+ }
+ }
+ qunlock(&hashlock);
+}
+
+int
+hashmboxrefs(Mailbox *mb)
+{
+ int h;
+ Hash *hp;
+ int refs = 0;
+
+ qlock(&hashlock);
+ for(h = 0; h < Hsize; h++){
+ for(hp = htab[h]; hp != nil; hp = hp->next)
+ if(hp->mb == mb)
+ refs++;
+ }
+ qunlock(&hashlock);
+ return refs;
+}
+
+void
+checkmboxrefs(void)
+{
+ int f, refs;
+ Mailbox *mb;
+
+ qlock(&mbllock);
+ for(mb=mbl; mb; mb=mb->next){
+ qlock(&mb->ql);
+ refs = (f=fidmboxrefs(mb))+1;
+ if(refs != mb->refs){
+ fprint(2, "mbox %s %s ref mismatch actual %d (%d+1) expected %d\n", mb->name, mb->path, refs, f, mb->refs);
+ abort();
+ }
+ qunlock(&mb->ql);
+ }
+ qunlock(&mbllock);
+}
+
+void
+post(char *name, char *envname, int srvfd)
+{
+ int fd;
+ char buf[32];
+
+ fd = create(name, OWRITE, 0600);
+ if(fd < 0)
+ error("post failed");
+ sprint(buf, "%d",srvfd);
+ if(write(fd, buf, strlen(buf)) != strlen(buf))
+ error("srv write");
+ close(fd);
+ putenv(envname, name);
+}