diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/vnc/exportfs.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/vnc/exportfs.c')
-rwxr-xr-x | sys/src/cmd/vnc/exportfs.c | 803 |
1 files changed, 803 insertions, 0 deletions
diff --git a/sys/src/cmd/vnc/exportfs.c b/sys/src/cmd/vnc/exportfs.c new file mode 100755 index 000000000..4948016d4 --- /dev/null +++ b/sys/src/cmd/vnc/exportfs.c @@ -0,0 +1,803 @@ +#include <u.h> +#include <libc.h> +#include <fcall.h> +#include "compat.h" +#include "error.h" + +typedef struct Fid Fid; +typedef struct Export Export; +typedef struct Exq Exq; +typedef struct Exwork Exwork; + +enum +{ + Nfidhash = 32, + Maxfdata = 8192, + Maxrpc = IOHDRSZ + Maxfdata, +}; + +struct Export +{ + Ref r; + Exq* work; + Lock fidlock; + Fid* fid[Nfidhash]; + int io; /* fd to read/write */ + int iounit; + int nroots; + Chan **roots; +}; + +struct Fid +{ + Fid* next; + Fid** last; + Chan* chan; + long offset; + int fid; + int ref; /* fcalls using the fid; locked by Export.Lock */ + int attached; /* fid attached or cloned but not clunked */ +}; + +struct Exq +{ + Lock lk; + int responding; /* writing out reply message */ + int noresponse; /* don't respond to this one */ + Exq* next; + int shut; /* has been noted for shutdown */ + Export* export; + void* slave; + Fcall rpc; + uchar buf[Maxrpc]; +}; + +struct Exwork +{ + Lock l; + + int ref; + + int nwaiters; /* queue of slaves waiting for work */ + QLock qwait; + Rendez rwait; + + Exq *head; /* work waiting for a slave */ + Exq *tail; +}; + +Exwork exq; + +static void exshutdown(Export*); +static void exflush(Export*, int, int); +static void exslave(void*); +static void exfree(Export*); +static void exportproc(Export*); + +static char* Exattach(Export*, Fcall*, uchar*); +static char* Exauth(Export*, Fcall*, uchar*); +static char* Exclunk(Export*, Fcall*, uchar*); +static char* Excreate(Export*, Fcall*, uchar*); +static char* Exversion(Export*, Fcall*, uchar*); +static char* Exopen(Export*, Fcall*, uchar*); +static char* Exread(Export*, Fcall*, uchar*); +static char* Exremove(Export*, Fcall*, uchar*); +static char* Exsession(Export*, Fcall*, uchar*); +static char* Exstat(Export*, Fcall*, uchar*); +static char* Exwalk(Export*, Fcall*, uchar*); +static char* Exwrite(Export*, Fcall*, uchar*); +static char* Exwstat(Export*, Fcall*, uchar*); + +static char *(*fcalls[Tmax])(Export*, Fcall*, uchar*); + +static char Enofid[] = "no such fid"; +static char Eseekdir[] = "can't seek on a directory"; +static char Ereaddir[] = "unaligned read of a directory"; +static int exdebug = 0; + +int +sysexport(int fd, Chan **roots, int nroots) +{ + Export *fs; + + fs = smalloc(sizeof(Export)); + fs->r.ref = 1; + fs->io = fd; + fs->roots = roots; + fs->nroots = nroots; + + exportproc(fs); + + return 0; +} + +static void +exportinit(void) +{ + lock(&exq.l); + exq.ref++; + if(fcalls[Tversion] != nil){ + unlock(&exq.l); + return; + } + + fmtinstall('F', fcallfmt); + fcalls[Tversion] = Exversion; + fcalls[Tauth] = Exauth; + fcalls[Tattach] = Exattach; + fcalls[Twalk] = Exwalk; + fcalls[Topen] = Exopen; + fcalls[Tcreate] = Excreate; + fcalls[Tread] = Exread; + fcalls[Twrite] = Exwrite; + fcalls[Tclunk] = Exclunk; + fcalls[Tremove] = Exremove; + fcalls[Tstat] = Exstat; + fcalls[Twstat] = Exwstat; + unlock(&exq.l); +} + +static void +exportproc(Export *fs) +{ + Exq *q; + int n, ed; + + exportinit(); + ed = errdepth(-1); + for(;;){ + errdepth(ed); + q = smalloc(sizeof(Exq)); + + n = read9pmsg(fs->io, q->buf, Maxrpc); + if(n <= 0 || convM2S(q->buf, n, &q->rpc) != n) + goto bad; + + if(exdebug) + print("export %d <- %F\n", getpid(), &q->rpc); + + if(q->rpc.type == Tflush){ + exflush(fs, q->rpc.tag, q->rpc.oldtag); + free(q); + continue; + } + + q->export = fs; + incref(&fs->r); + + lock(&exq.l); + if(exq.head == nil) + exq.head = q; + else + exq.tail->next = q; + q->next = nil; + exq.tail = q; + n = exq.nwaiters; + if(n) + exq.nwaiters = n - 1; + unlock(&exq.l); + if(!n) + kproc("exportfs", exslave, nil); + rendwakeup(&exq.rwait); + } +bad: + free(q); + if(exdebug) + fprint(2, "export proc shutting down: %r\n"); + exshutdown(fs); + exfree(fs); +} + +static void +exflush(Export *fs, int flushtag, int tag) +{ + Exq *q, **last; + Fcall fc; + uchar buf[Maxrpc]; + int n; + + /* hasn't been started? */ + lock(&exq.l); + last = &exq.head; + for(q = exq.head; q != nil; q = q->next){ + if(q->export == fs && q->rpc.tag == tag){ + *last = q->next; + unlock(&exq.l); + exfree(fs); + free(q); + goto Respond; + } + last = &q->next; + } + unlock(&exq.l); + + /* in progress? */ + lock(&fs->r); + for(q = fs->work; q != nil; q = q->next){ + if(q->rpc.tag == tag){ + lock(&q->lk); + q->noresponse = 1; + if(!q->responding) + rendintr(q->slave); + unlock(&q->lk); + break; + } + } + unlock(&fs->r); + +Respond: + fc.type = Rflush; + fc.tag = flushtag; + + n = convS2M(&fc, buf, Maxrpc); + if(n == 0) + panic("convS2M error on write"); + if(write(fs->io, buf, n) != n) + panic("mount write"); +} + +static void +exshutdown(Export *fs) +{ + Exq *q, **last; + + lock(&exq.l); + last = &exq.head; + for(q = exq.head; q != nil; q = *last){ + if(q->export == fs){ + *last = q->next; + exfree(fs); + free(q); + continue; + } + last = &q->next; + } + + /* + * cleanly shut down the slaves if this is the last fs around + */ + exq.ref--; + if(!exq.ref) + rendwakeup(&exq.rwait); + unlock(&exq.l); + + /* + * kick any sleepers + */ + lock(&fs->r); + for(q = fs->work; q != nil; q = q->next){ + lock(&q->lk); + q->noresponse = 1; + if(!q->responding) + rendintr(q->slave); + unlock(&q->lk); + } + unlock(&fs->r); +} + +static void +exfree(Export *fs) +{ + Fid *f, *n; + int i; + + if(decref(&fs->r) != 0) + return; + for(i = 0; i < Nfidhash; i++){ + for(f = fs->fid[i]; f != nil; f = n){ + if(f->chan != nil) + cclose(f->chan); + n = f->next; + free(f); + } + } + free(fs); +} + +static int +exwork(void *) +{ + int work; + + lock(&exq.l); + work = exq.head != nil || !exq.ref; + unlock(&exq.l); + return work; +} + +static void +exslave(void *) +{ + Export *fs; + Exq *q, *t, **last; + char *volatile err; + int n, ed; + + while(waserror()) + fprint(2, "exslave %d errored out of loop -- heading back in!\n", getpid()); + ed = errdepth(-1); + for(;;){ + errdepth(ed); + qlock(&exq.qwait); + if(waserror()){ + qunlock(&exq.qwait); + nexterror(); + } + rendsleep(&exq.rwait, exwork, nil); + + lock(&exq.l); + if(!exq.ref){ + unlock(&exq.l); + poperror(); + qunlock(&exq.qwait); + break; + } + q = exq.head; + if(q == nil){ + unlock(&exq.l); + poperror(); + qunlock(&exq.qwait); + continue; + } + exq.head = q->next; + if(exq.head == nil) + exq.tail = nil; + poperror(); + qunlock(&exq.qwait); + + /* + * put the job on the work queue before it's + * visible as off of the head queue, so it's always + * findable for flushes and shutdown + */ + q->slave = up; + q->noresponse = 0; + q->responding = 0; + rendclearintr(); + fs = q->export; + lock(&fs->r); + q->next = fs->work; + fs->work = q; + unlock(&fs->r); + + unlock(&exq.l); + + if(exdebug > 1) + print("exslave dispatch %d %F\n", getpid(), &q->rpc); + + if(waserror()){ + print("exslave err %r\n"); + err = up->error; + }else{ + if(q->rpc.type >= Tmax || !fcalls[q->rpc.type]) + err = "bad fcall type"; + else + err = (*fcalls[q->rpc.type])(fs, &q->rpc, &q->buf[IOHDRSZ]); + poperror(); + } + + q->rpc.type++; + if(err){ + q->rpc.type = Rerror; + q->rpc.ename = err; + } + n = convS2M(&q->rpc, q->buf, Maxrpc); + + if(exdebug) + print("exslave %d -> %F\n", getpid(), &q->rpc); + + lock(&q->lk); + if(!q->noresponse){ + q->responding = 1; + unlock(&q->lk); + write(fs->io, q->buf, n); + }else + unlock(&q->lk); + + /* + * exflush might set noresponse at this point, but + * setting noresponse means don't send a response now; + * it's okay that we sent a response already. + */ + if(exdebug > 1) + print("exslave %d written %d\n", getpid(), q->rpc.tag); + + lock(&fs->r); + last = &fs->work; + for(t = fs->work; t != nil; t = t->next){ + if(t == q){ + *last = q->next; + break; + } + last = &t->next; + } + unlock(&fs->r); + + exfree(q->export); + free(q); + + rendclearintr(); + lock(&exq.l); + exq.nwaiters++; + unlock(&exq.l); + } + if(exdebug) + fprint(2, "export slaveshutting down\n"); + kexit(); +} + +Fid* +Exmkfid(Export *fs, int fid) +{ + ulong h; + Fid *f, *nf; + + nf = mallocz(sizeof(Fid), 1); + if(nf == nil) + return nil; + lock(&fs->fidlock); + h = fid % Nfidhash; + for(f = fs->fid[h]; f != nil; f = f->next){ + if(f->fid == fid){ + unlock(&fs->fidlock); + free(nf); + return nil; + } + } + + nf->next = fs->fid[h]; + if(nf->next != nil) + nf->next->last = &nf->next; + nf->last = &fs->fid[h]; + fs->fid[h] = nf; + + nf->fid = fid; + nf->ref = 1; + nf->attached = 1; + nf->offset = 0; + nf->chan = nil; + unlock(&fs->fidlock); + return nf; +} + +Fid* +Exgetfid(Export *fs, int fid) +{ + Fid *f; + ulong h; + + lock(&fs->fidlock); + h = fid % Nfidhash; + for(f = fs->fid[h]; f; f = f->next){ + if(f->fid == fid){ + if(f->attached == 0) + break; + f->ref++; + unlock(&fs->fidlock); + return f; + } + } + unlock(&fs->fidlock); + return nil; +} + +void +Exputfid(Export *fs, Fid *f) +{ + lock(&fs->fidlock); + f->ref--; + if(f->ref == 0 && f->attached == 0){ + if(f->chan != nil) + cclose(f->chan); + f->chan = nil; + *f->last = f->next; + if(f->next != nil) + f->next->last = f->last; + unlock(&fs->fidlock); + free(f); + return; + } + unlock(&fs->fidlock); +} + +static char* +Exversion(Export *fs, Fcall *rpc, uchar *) +{ + if(rpc->msize > Maxrpc) + rpc->msize = Maxrpc; + if(strncmp(rpc->version, "9P", 2) != 0){ + rpc->version = "unknown"; + return nil; + } + + fs->iounit = rpc->msize - IOHDRSZ; + rpc->version = "9P2000"; + return nil; +} + +static char* +Exauth(Export *, Fcall *, uchar *) +{ + return "vnc: authentication not required"; +} + +static char* +Exattach(Export *fs, Fcall *rpc, uchar *) +{ + Fid *f; + int w; + + w = 0; + if(rpc->aname != nil) + w = strtol(rpc->aname, nil, 10); + if(w < 0 || w > fs->nroots) + error(Ebadspec); + f = Exmkfid(fs, rpc->fid); + if(f == nil) + return Einuse; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->error; + } + f->chan = cclone(fs->roots[w]); + poperror(); + rpc->qid = f->chan->qid; + Exputfid(fs, f); + return nil; +} + +static char* +Exclunk(Export *fs, Fcall *rpc, uchar *) +{ + Fid *f; + + f = Exgetfid(fs, rpc->fid); + if(f != nil){ + f->attached = 0; + Exputfid(fs, f); + } + return nil; +} + +static char* +Exwalk(Export *fs, Fcall *rpc, uchar *) +{ + Fid *volatile f, *volatile nf; + Walkqid *wq; + Chan *c; + int i, nwname; + int volatile isnew; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + nf = nil; + if(waserror()){ + Exputfid(fs, f); + if(nf != nil) + Exputfid(fs, nf); + return up->error; + } + + /* + * optional clone, but don't attach it until the walk succeeds. + */ + if(rpc->fid != rpc->newfid){ + nf = Exmkfid(fs, rpc->newfid); + if(nf == nil) + error(Einuse); + nf->attached = 0; + isnew = 1; + }else{ + nf = Exgetfid(fs, rpc->fid); + isnew = 0; + } + + /* + * let the device do the work + */ + c = f->chan; + nwname = rpc->nwname; + wq = (*devtab[c->type]->walk)(c, nf->chan, rpc->wname, nwname); + if(wq == nil) + error(Enonexist); + + poperror(); + + /* + * copy qid array + */ + for(i = 0; i < wq->nqid; i++) + rpc->wqid[i] = wq->qid[i]; + rpc->nwqid = wq->nqid; + + /* + * update the channel if everything walked correctly. + */ + if(isnew && wq->nqid == nwname){ + nf->chan = wq->clone; + nf->attached = 1; + } + + free(wq); + Exputfid(fs, f); + Exputfid(fs, nf); + return nil; +} + +static char* +Exopen(Export *fs, Fcall *rpc, uchar *) +{ + Fid *volatile f; + Chan *c; + int iou; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->error; + } + c = f->chan; + c = (*devtab[c->type]->open)(c, rpc->mode); + poperror(); + + f->chan = c; + f->offset = 0; + rpc->qid = f->chan->qid; + iou = f->chan->iounit; + if(iou > fs->iounit) + iou = fs->iounit; + rpc->iounit = iou; + Exputfid(fs, f); + return nil; +} + +static char* +Excreate(Export *fs, Fcall *rpc, uchar *) +{ + Fid *f; + Chan *c; + int iou; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->error; + } + c = f->chan; + (*devtab[c->type]->create)(c, rpc->name, rpc->mode, rpc->perm); + poperror(); + + f->chan = c; + rpc->qid = f->chan->qid; + iou = f->chan->iounit; + if(iou > fs->iounit) + iou = fs->iounit; + rpc->iounit = iou; + Exputfid(fs, f); + return nil; +} + +static char* +Exread(Export *fs, Fcall *rpc, uchar *buf) +{ + Fid *f; + Chan *c; + long off; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + + c = f->chan; + + if(waserror()){ + Exputfid(fs, f); + return up->error; + } + + rpc->data = (char*)buf; + off = rpc->offset; + c->offset = off; + rpc->count = (*devtab[c->type]->read)(c, rpc->data, rpc->count, off); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exwrite(Export *fs, Fcall *rpc, uchar *) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->error; + } + c = f->chan; + if(c->qid.type & QTDIR) + error(Eisdir); + rpc->count = (*devtab[c->type]->write)(c, rpc->data, rpc->count, rpc->offset); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exstat(Export *fs, Fcall *rpc, uchar *buf) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->error; + } + c = f->chan; + rpc->stat = buf; + rpc->nstat = (*devtab[c->type]->stat)(c, rpc->stat, Maxrpc); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exwstat(Export *fs, Fcall *rpc, uchar *) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->error; + } + c = f->chan; + (*devtab[c->type]->wstat)(c, rpc->stat, rpc->nstat); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exremove(Export *fs, Fcall *rpc, uchar *) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->error; + } + c = f->chan; + (*devtab[c->type]->remove)(c); + poperror(); + + /* + * chan is already clunked by remove. + * however, we need to recover the chan, + * and follow sysremove's lead in making to point to root. + */ + c->type = 0; + + f->attached = 0; + Exputfid(fs, f); + return nil; +} |