From 5cdb17983ae6e6367ad7a940cb219eab247a9304 Mon Sep 17 00:00:00 2001 From: rsc Date: Sat, 29 Oct 2005 16:26:44 +0000 Subject: Thanks to John Cummings. --- src/cmd/upas/fs/fs.c | 1704 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1704 insertions(+) create mode 100644 src/cmd/upas/fs/fs.c (limited to 'src/cmd/upas/fs/fs.c') 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 +#include +#include +#include <9pclient.h> /* jpc */ +#include /* 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); +} -- cgit v1.2.3