summaryrefslogtreecommitdiff
path: root/sys/src/cmd/unix/drawterm/kern/exportfs.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /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-xsys/src/cmd/unix/drawterm/kern/exportfs.c821
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;
+}