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/unix/drawterm/kern/exportfs.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/unix/drawterm/kern/exportfs.c')
-rwxr-xr-x | sys/src/cmd/unix/drawterm/kern/exportfs.c | 821 |
1 files changed, 821 insertions, 0 deletions
diff --git a/sys/src/cmd/unix/drawterm/kern/exportfs.c b/sys/src/cmd/unix/drawterm/kern/exportfs.c new file mode 100755 index 000000000..46cb90d0c --- /dev/null +++ b/sys/src/cmd/unix/drawterm/kern/exportfs.c @@ -0,0 +1,821 @@ +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" + +typedef struct Fid Fid; +typedef struct Export Export; +typedef struct Exq Exq; + +#define nil ((void*)0) + +enum +{ + Nfidhash = 1, + MAXRPC = MAXMSG+MAXFDATA, + MAXDIRREAD = (MAXFDATA/DIRLEN)*DIRLEN +}; + +struct Export +{ + Ref r; + Exq* work; + Lock fidlock; + Fid* fid[Nfidhash]; + Chan* root; + Chan* io; + Pgrp* pgrp; + int npart; + char part[MAXRPC]; +}; + +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 nointr; + int noresponse; /* don't respond to this one */ + Exq* next; + int shut; /* has been noted for shutdown */ + Export* export; + void* slave; + Fcall rpc; + char buf[MAXRPC]; +}; + +struct +{ + Lock l; + Qlock qwait; + Rendez rwait; + Exq *head; /* work waiting for a slave */ + Exq *tail; +}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* Exauth(Export*, Fcall*); +static char* Exattach(Export*, Fcall*); +static char* Exclunk(Export*, Fcall*); +static char* Excreate(Export*, Fcall*); +static char* Exopen(Export*, Fcall*); +static char* Exread(Export*, Fcall*); +static char* Exremove(Export*, Fcall*); +static char* Exstat(Export*, Fcall*); +static char* Exwalk(Export*, Fcall*); +static char* Exwrite(Export*, Fcall*); +static char* Exwstat(Export*, Fcall*); +static char* Exversion(Export*, Fcall*); + +static char *(*fcalls[Tmax])(Export*, Fcall*); + +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 *c; + Export *fs; + + if(waserror()) + return -1; + + c = fdtochan(fd, ORDWR, 1, 1); + poperror(); + c->flag |= CMSG; + + fs = mallocz(sizeof(Export)); + fs->r.ref = 1; + fs->pgrp = up->pgrp; + refinc(&fs->pgrp->r); + refinc(&up->slash->r); + fs->root = up->slash; + refinc(&fs->root->r); + fs->root = domount(fs->root); + fs->io = c; + + exportproc(fs); + + return 0; +} + +static void +exportinit(void) +{ + lock(&exq.l); + if(fcalls[Tversion] != nil) { + unlock(&exq.l); + return; + } + + fmtinstall('F', fcallfmt); + fmtinstall('D', dirfmt); + fmtinstall('M', dirmodefmt); + 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); +} + +void +exportproc(Export *fs) +{ + Exq *q; + char *buf; + int n, cn, len; + + exportinit(); + + for(;;){ + q = mallocz(sizeof(Exq)); + if(q == 0) + panic("no memory"); + + q->rpc.data = q->buf + MAXMSG; + + buf = q->buf; + len = MAXRPC; + if(fs->npart) { + memmove(buf, fs->part, fs->npart); + buf += fs->npart; + len -= fs->npart; + goto chk; + } + for(;;) { + if(waserror()) + goto bad; + + n = (*devtab[fs->io->type].read)(fs->io, buf, len, 0); + poperror(); + + if(n <= 0) + goto bad; + + buf += n; + len -= n; + chk: + n = buf - q->buf; + + /* convM2S returns size of correctly decoded message */ + cn = convM2S(q->buf, &q->rpc, n); + if(cn < 0){ + iprint("bad message type in devmnt\n"); + goto bad; + } + if(cn > 0) { + n -= cn; + if(n < 0){ + iprint("negative size in devmnt"); + goto bad; + } + fs->npart = n; + if(n != 0) + memmove(fs->part, q->buf+cn, n); + break; + } + } + if(exdebug) + iprint("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; + refinc(&fs->r); + + lock(&exq.l); + if(exq.head == nil) + exq.head = q; + else + exq.tail->next = q; + q->next = nil; + exq.tail = q; + unlock(&exq.l); + if(exq.qwait.first == nil) { + n = thread("exportfs", exslave, nil); +/* iprint("launch export (pid=%ux)\n", n); */ + } + rendwakeup(&exq.rwait); + } +bad: + free(q); + exshutdown(fs); + exfree(fs); +} + +void +exflush(Export *fs, int flushtag, int tag) +{ + Exq *q, **last; + int n; + Fcall fc; + char buf[MAXMSG]; + + /* 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.l); + for(q = fs->work; q != nil; q = q->next){ + if(q->rpc.tag == tag && !q->noresponse){ + lock(&q->lk); + q->noresponse = 1; + if(!q->nointr) + intr(q->slave); + unlock(&q->lk); + unlock(&fs->r.l); + goto Respond; + return; + } + } + unlock(&fs->r.l); + +if(exdebug) iprint("exflush: did not find rpc: %d\n", tag); + +Respond: + fc.type = Rflush; + fc.tag = flushtag; + n = convS2M(&fc, buf); +if(exdebug) iprint("exflush -> %F\n", &fc); + if(!waserror()){ + (*devtab[fs->io->type].write)(fs->io, buf, n, 0); + poperror(); + } +} + +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; + } + unlock(&exq.l); + + lock(&fs->r.l); + q = fs->work; + while(q != nil){ + if(q->shut){ + q = q->next; + continue; + } + q->shut = 1; + unlock(&fs->r.l); + /* postnote(q->slave, 1, "interrupted", NUser); */ + iprint("postnote 2!\n"); + lock(&fs->r.l); + q = fs->work; + } + unlock(&fs->r.l); +} + +void +exfree(Export *fs) +{ + Fid *f, *n; + int i; + + if(refdec(&fs->r) != 0) + return; + closepgrp(fs->pgrp); + cclose(fs->root); + cclose(fs->io); + 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); +} + +int +exwork(void *a) +{ + return exq.head != nil; +} + +void +exslave(void *a) +{ + Export *fs; + Exq *q, *t, **last; + char *err; + int n; +/* + closepgrp(up->pgrp); + up->pgrp = nil; +*/ + for(;;){ + qlock(&exq.qwait); + rendsleep(&exq.rwait, exwork, nil); + + lock(&exq.l); + q = exq.head; + if(q == nil) { + unlock(&exq.l); + qunlock(&exq.qwait); + continue; + } + exq.head = q->next; + q->slave = curthread(); + unlock(&exq.l); + + qunlock(&exq.qwait); + + q->noresponse = 0; + q->nointr = 0; + fs = q->export; + lock(&fs->r.l); + q->next = fs->work; + fs->work = q; + unlock(&fs->r.l); + + up->pgrp = q->export->pgrp; + + if(exdebug > 1) + iprint("exslave dispatch %d %F\n", getpid(), &q->rpc); + + if(waserror()){ + iprint("exslave err %r\n"); + err = up->errstr; + goto Err; + } + if(q->rpc.type >= Tmax || !fcalls[q->rpc.type]) + err = "bad fcall type"; + else + err = (*fcalls[q->rpc.type])(fs, &q->rpc); + + poperror(); + Err:; + q->rpc.type++; + if(err){ + q->rpc.type = Rerror; + strncpy(q->rpc.ename, err, ERRLEN); + } + n = convS2M(&q->rpc, q->buf); + + if(exdebug) + iprint("exslve %d -> %F\n", getpid(), &q->rpc); + + lock(&q->lk); + if(q->noresponse == 0){ + q->nointr = 1; + clearintr(); + if(!waserror()){ + (*devtab[fs->io->type].write)(fs->io, q->buf, n, 0); + poperror(); + } + } + 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) + iprint("exslave %d written %d\n", getpid(), q->rpc.tag); + + lock(&fs->r.l); + 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.l); + + exfree(q->export); + free(q); + } + iprint("exslave shut down"); + threadexit(); +} + +Fid* +Exmkfid(Export *fs, int fid) +{ + ulong h; + Fid *f, *nf; + + nf = mallocz(sizeof(Fid)); + 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); +} + +char* +Exsession(Export *e, Fcall *rpc) +{ + memset(rpc->authid, 0, sizeof(rpc->authid)); + memset(rpc->authdom, 0, sizeof(rpc->authdom)); + memset(rpc->chal, 0, sizeof(rpc->chal)); + return nil; +} + +char* +Exauth(Export *e, Fcall *f) +{ + return "authentication not required"; +} + +char* +Exattach(Export *fs, Fcall *rpc) +{ + Fid *f; + + f = Exmkfid(fs, rpc->fid); + if(f == nil) + return Einuse; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->errstr; + } + f->chan = clone(fs->root, nil); + poperror(); + rpc->qid = f->chan->qid; + Exputfid(fs, f); + return nil; +} + +char* +Exclone(Export *fs, Fcall *rpc) +{ + Fid *f, *nf; + + if(rpc->fid == rpc->newfid) + return Einuse; + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + nf = Exmkfid(fs, rpc->newfid); + if(nf == nil){ + Exputfid(fs, f); + return Einuse; + } + if(waserror()){ + Exputfid(fs, f); + Exputfid(fs, nf); + return up->errstr; + } + nf->chan = clone(f->chan, nil); + poperror(); + Exputfid(fs, f); + Exputfid(fs, nf); + return nil; +} + +char* +Exclunk(Export *fs, Fcall *rpc) +{ + Fid *f; + + f = Exgetfid(fs, rpc->fid); + if(f != nil){ + f->attached = 0; + Exputfid(fs, f); + } + return nil; +} + +char* +Exwalk(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = walk(f->chan, rpc->name, 1); + if(c == nil) + error(Enonexist); + poperror(); + + f->chan = c; + rpc->qid = c->qid; + Exputfid(fs, f); + return nil; +} + +char* +Exopen(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + c = (*devtab[c->type].open)(c, rpc->mode); + poperror(); + + f->chan = c; + f->offset = 0; + rpc->qid = f->chan->qid; + Exputfid(fs, f); + return nil; +} + +char* +Excreate(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + if(c->mnt && !(c->flag&CCREATE)) + c = createdir(c); + (*devtab[c->type].create)(c, rpc->name, rpc->mode, rpc->perm); + poperror(); + + f->chan = c; + rpc->qid = f->chan->qid; + Exputfid(fs, f); + return nil; +} + +char* +Exread(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + long off; + int dir, n, seek; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + + c = f->chan; + dir = c->qid.path & CHDIR; + if(dir){ + rpc->count -= rpc->count%DIRLEN; + if(rpc->offset%DIRLEN || rpc->count==0){ + Exputfid(fs, f); + return Ereaddir; + } + if(f->offset > rpc->offset){ + Exputfid(fs, f); + return Eseekdir; + } + } + + if(waserror()) { + Exputfid(fs, f); + return up->errstr; + } + + for(;;){ + n = rpc->count; + seek = 0; + off = rpc->offset; + if(dir && f->offset != off){ + off = f->offset; + n = rpc->offset - off; + if(n > MAXDIRREAD) + n = MAXDIRREAD; + seek = 1; + } + if(dir && c->mnt != nil) + n = unionread(c, rpc->data, n); + else{ + c->offset = off; + n = (*devtab[c->type].read)(c, rpc->data, n, off); + } + if(n == 0 || !seek) + break; + f->offset = off + n; + c->offset += n; + } + rpc->count = n; + poperror(); + Exputfid(fs, f); + return nil; +} + +char* +Exwrite(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + if(c->qid.path & CHDIR) + error(Eisdir); + rpc->count = (*devtab[c->type].write)(c, rpc->data, rpc->count, rpc->offset); + poperror(); + Exputfid(fs, f); + return nil; +} + +char* +Exstat(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + (*devtab[c->type].stat)(c, rpc->stat); + poperror(); + Exputfid(fs, f); + return nil; +} + +char* +Exwstat(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + (*devtab[c->type].wstat)(c, rpc->stat); + poperror(); + Exputfid(fs, f); + return nil; +} + +char* +Exremove(Export *fs, Fcall *rpc) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, rpc->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + 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; +} |