This patch adds export (exportfs) system call. The original patch was done by rsc (Russ) and brucee for Ranbgoom in 2006 and re-applied to the latest Labs kernel in early 2015 (4/2015) by me. exportfs.c was adapted from Inferno. Reference: /n/sources/patch/syscall-exportfs Date: Thu Mar 17 01:53:21 CET 2016 Signed-off-by: skip.tavakkolian@gmail.com --- /sys/include/libc.h Thu Mar 17 01:51:59 2016 +++ /sys/include/libc.h Thu Mar 17 01:51:56 2016 @@ -365,6 +365,7 @@ extern int enc16(char*, int, uchar*, int); extern int encodefmt(Fmt*); extern void exits(char*); +extern int export(int, char*, int); extern double frexp(double, int*); extern uintptr getcallerpc(void*); extern char* getenv(char*); --- /sys/src/9/pc/mkfile Thu Mar 17 01:52:03 2016 +++ /sys/src/9/pc/mkfile Thu Mar 17 01:52:01 2016 @@ -27,6 +27,7 @@ chan.$O\ dev.$O\ edf.$O\ + exportfs.$O\ fault.$O\ latin1.$O\ page.$O\ --- /sys/src/9/port/chan.c Thu Mar 17 01:52:10 2016 +++ /sys/src/9/port/chan.c Thu Mar 17 01:52:05 2016 @@ -400,7 +400,7 @@ return p; } -static Path* +Path* addelem(Path *p, char *s, Chan *from) { char *t; @@ -959,10 +959,11 @@ /* * Either walks all the way or not at all. No partial results in *cp. * *nerror is the number of names to display in an error message. + * *ninfo is the number of chaninfos filled in. */ static char Edoesnotexist[] = "does not exist"; int -walk(Chan **cp, char **names, int nnames, int nomount, int *nerror) +walkq(Chan **cp, char **names, int nnames, int nomount, int *nerror, Chaninfo *info, int *ninfo) { int dev, didmount, dotdot, i, n, nhave, ntry, type; Chan *c, *nc, *mtpt; @@ -1000,6 +1001,8 @@ strcpy(up->errstr, Enotdir); if(mh != nil) putmhead(mh); + if(ninfo) + *ninfo = nhave; return -1; } ntry = nnames - nhave; @@ -1045,12 +1048,21 @@ pathclose(path); if(nerror) *nerror = nhave+1; + if(ninfo) + *ninfo = nhave; if(mh != nil) putmhead(mh); return -1; } } + if(info){ + for(i=0; inqid; i++){ + info[nhave+i].qid = wq->qid[i]; + info[nhave+i].type = type; + info[nhave+i].dev = dev; + } + } didmount = 0; if(dotdot){ assert(wq->nqid == 1); @@ -1084,6 +1096,8 @@ *nerror = nhave+wq->nqid; strcpy(up->errstr, Enotdir); } + if(ninfo) + *ninfo = nhave; free(wq); if(mh != nil) putmhead(mh); @@ -1130,7 +1144,14 @@ *cp = c; if(nerror) *nerror = nhave; + if(ninfo) + *ninfo = nhave; return 0; +} + +walk(Chan **cp, char **names, int nnames, int nomount, int *nerror) +{ + return walkq(cp, names, nnames, nomount, nerror, nil, nil); } /* --- /sys/src/9/port/exportfs.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/port/exportfs.c Thu Mar 17 01:52:12 2016 @@ -0,0 +1,1417 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +typedef struct Fid Fid; +typedef struct Export Export; +typedef struct Exq Exq; +typedef struct Uqid Uqid; + +enum +{ + Nfidhash = 32, + Nqidhash = 32, + QIDMASK = ((vlong)1<<48)-1, + MAXFDATA = 8192, + MAXRPC = IOHDRSZ+MAXFDATA, + MSGHDRSZ = BIT32SZ+BIT8SZ+BIT16SZ +}; + +struct Export +{ + Lock; + Ref r; + Exq* work; + Lock fidlock; + Fid* fid[Nfidhash]; + QLock qidlock; + Uqid* qids[Nqidhash]; + ulong pathgen; + Chan* io; + Chan* root; + Pgrp* pgrp; + Egrp* egrp; + Fgrp* fgrp; + int async; + int readonly; + int msize; + char* user; +}; + +struct Fid +{ + Fid* next; + Fid** last; + Chan* chan; + int fid; + int ref; /* fcalls using the fid; locked by Export.Lock */ + vlong offset; /* last offset used (within directory) */ + int attached; /* fid attached or cloned but not clunked */ + Uqid* qid; /* generated qid */ +}; + +struct Uqid +{ + Ref; + int type; + int dev; + vlong oldpath; + vlong newpath; + Uqid* next; +}; + +struct Exq +{ + Lock; + int busy; /* fcall in progress */ + int finished; /* will do no more work on this request or flushes */ + Exq* next; + int shut; /* has been noted for shutdown */ + Exq* flush; /* queued flush requests */ + Exq* flusht; /* tail of flush queue */ + Export* export; + Proc* slave; + Fcall in, out; +/* int msize; */ + uchar buf[MAXRPC]; +}; + +struct +{ + Lock l; + QLock qwait; + Rendez rwait; + Exq *head; /* work waiting for a slave */ + Exq *tail; +}exq; + +static void exshutdown(Export*); +static int exflushed(Export*, Exq*); +static void exslave(void*); +static void exfree(Export*); +static void exfreeq(Exq*); +static void exportproc(void*); +static void exreply(Exq*, char*); +static int exisroot(Export*, Chan*); +static Uqid* uqidalloc(Export*, Chaninfo*); +static void freeuqid(Export*, Uqid*); +static void swiproc(Proc*, int); +static void notkilled(void); + +extern Chan* cunique(Chan*); +extern Chan* createdir(Chan*, Mhead*); +extern Path* addelem(Path*, char*, Chan*); + +static char* Exversion(Export*, Fcall*, Fcall*); +static char* Exauth(Export*, Fcall*, Fcall*); +static char* Exattach(Export*, Fcall*, Fcall*); +static char* Exclunk(Export*, Fcall*, Fcall*); +static char* Excreate(Export*, Fcall*, Fcall*); +static char* Exopen(Export*, Fcall*, Fcall*); +static char* Exread(Export*, Fcall*, Fcall*); +static char* Exremove(Export*, Fcall*, Fcall*); +static char* Exstat(Export*, Fcall*, Fcall*); +static char* Exwalk(Export*, Fcall*, Fcall*); +static char* Exwrite(Export*, Fcall*, Fcall*); +static char* Exwstat(Export*, Fcall*, Fcall*); + +static char *(*fcalls[Tmax])(Export*, Fcall*, Fcall*); +static char *tnames[Tmax]; + +static char Enofid[] = "no such fid"; +static char Eseekdir[] = "can't seek on a directory"; +static char Eopen[] = "walk of open fid"; +static char Emode[] = "open/create -- unknown mode"; +static char Edupfid[] = "fid in use"; +static char Eaccess[] = "read/write -- not open in suitable mode"; +static char Ecount[] = "read/write -- count too big"; +int exdebug = 0; + +static void kwerrstr(char*, ...); +#pragma varargck argpos kwerrstr 1 +int export(int, char*, int); + +long +sysexport(ulong *arg) +{ + int fd; + char *dir; + int async; + + fd = arg[0]; + validaddr(arg[1], 1, 0); + dir = (char*)arg[1]; + async = arg[2]; + return export(fd, dir, async); +} + +int +export(int fd, char *dir, int async) +{ + Chan *c, *dc; + Pgrp *pg; + Egrp *eg; + Export *fs; + + c = fdtochan(fd, ORDWR, 1, 1); + if(waserror()){ + cclose(c); + nexterror(); + } + dc = namec(dir, Atodir, 0, 0); + if(waserror()){ + cclose(dc); + nexterror(); + } + + fs = malloc(sizeof(Export)); + if(fs == nil) + error(Enomem); + + fs->r.ref = 1; + pg = up->pgrp; + fs->pgrp = pg; + incref(pg); + eg = up->egrp; + fs->egrp = eg; + if(eg != nil) + incref(eg); + fs->fgrp = dupfgrp(nil); + kstrdup(&fs->user, up->user); + fs->root = dc; + fs->io = c; + fs->pathgen = 0; + fs->msize = 0; + c->flag |= CMSG; + fs->async = async; + + if(async){ + kproc("exportfs", exportproc, fs); + poperror(); + poperror(); + }else{ + poperror(); + poperror(); + exportproc(fs); + } + return 0; +} + +static void +exportinit(void) +{ + lock(&exq.l); + if(fcalls[Tversion] != nil) { + unlock(&exq.l); + return; + } + 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; + + tnames[Tversion] = "Tversion"; + tnames[Tauth] = "Tauth"; + tnames[Tattach] = "Tattach"; + tnames[Twalk] = "Twalk"; + tnames[Topen] = "Topen"; + tnames[Tcreate] = "Tcreate"; + tnames[Tread] = "Tread"; + tnames[Twrite] = "Twrite"; + tnames[Tclunk] = "Tclunk"; + tnames[Tremove] = "Tremove"; + tnames[Tstat] = "Tstat"; + tnames[Twstat] = "Twstat"; + + unlock(&exq.l); +} + +static int +exisroot(Export *fs, Chan *c) +{ + return eqchan(fs->root, c, 1); +} + +static int +exreadn(Chan *c, void *buf, int n) +{ + int nr, t; + + if(waserror()) + return -1; + for(nr = 0; nr < n;){ + t = devtab[c->type]->read(c, (char*)buf+nr, n-nr, 0); + if(t <= 0) + break; + nr += t; + } + poperror(); + return nr; +} + +static int +exreadmsg(Chan *c, void *a, uint n) +{ + int m, len; + uchar *buf; + + buf = a; + m = exreadn(c, buf, BIT32SZ); + if(m < BIT32SZ){ + if(m < 0) + return -1; + return 0; + } + len = GBIT32(buf); + if(len <= BIT32SZ || len > n){ + kwerrstr("bad length in 9P message header"); + return -1; + } + len -= BIT32SZ; + m = exreadn(c, buf+BIT32SZ, len); + if(m < len){ + if(m < 0) + return -1; + return 0; + } + return BIT32SZ+m; +} + +static void +exportproc(void *a) +{ + Exq *q; + int async; + int n, type; + Export *fs = a; + + exportinit(); + fmtinstall('H', encodefmt); + + for(;;){ + for(n=0;; n++){ + q = mallocz(sizeof(Exq), 0); /* we don't use smalloc to avoid memset */ + if(q != nil || n > 6000) + break; + if(n%600 == 0) + print("exportproc %ld: waiting for memory for request\n", up->pid); + tsleep(&up->sleep, return0, nil, 100); + } + if(q == nil){ + kwerrstr("out of memory: read request"); + n = -1; + break; + } + memset(q, 0, sizeof(*q)-sizeof(q->buf)); + + n = exreadmsg(fs->io, q->buf, MAXRPC); /* TO DO: avoid copy */ + if(n <= 0) + break; + if(convM2S(q->buf, n, &q->in) != n){ + print("bad T: %.*H\n", n > 30 ? 30 : n, q->buf); + kwerrstr("bad T-message"); + n = -1; + break; + } + type = q->in.type; + if(type < Tversion || type >= Tmax || (type&1) || type == Terror){ + kwerrstr("invalid T-message type %d", type); + n = -1; + break; + } + + if(exdebug) + print("export %ld <- %F\n", up->pid, &q->in); + + q->out.type = type+1; + q->out.tag = q->in.tag; + + q->export = fs; + incref(&fs->r); + + if(fs->readonly){ + switch(type){ + case Topen: + if((q->in.mode & (ORCLOSE|OTRUNC|3)) == OREAD) + break; + /* FALL THROUGH */ + case Tcreate: + case Twrite: + case Tremove: + case Twstat: + q->out.type = Rerror; + q->out.ename = "file system read only"; + exreply(q, "exportproc"); + exfreeq(q); + continue; + } + } + + if(q->in.type == Tflush){ + if(exflushed(fs, q)){ + /* not yet started or not found (flush arrived after reply); we reply */ + if(exdebug) + print("export: flush %d\n", q->in.oldtag); + exreply(q, "exportproc"); + exfreeq(q); + } + continue; + } + + 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.head == nil) + kproc("exslave", exslave, nil); + wakeup(&exq.rwait); + } + + if(exdebug){ + if(n < 0) + print("exportproc %ld shut down: %s\n", up->pid, up->errstr); + else + print("exportproc %ld shut down\n", up->pid); + } + + free(q); + exshutdown(fs); + async = fs->async; + exfree(fs); + + if(async) + pexit("mount shut down", 0); +} + +static int +exflushed(Export *fs, Exq *fq) +{ + Exq *q, **last; + ulong pid; + + /* not yet started? */ + lock(&exq.l); + for(last = &exq.head; (q = *last) != nil; last = &q->next) + if(q->export == fs && q->in.tag == fq->in.oldtag){ + *last = q->next; + unlock(&exq.l); + /* not yet started: discard, and Rflush */ + exfreeq(q); + return 1; + } + unlock(&exq.l); + + /* tricky case: in progress */ + lock(fs); + for(q = fs->work; q != nil; q = q->next) + if(q->in.tag == fq->in.oldtag){ + pid = 0; + lock(q); + if(q->finished){ + /* slave replied and emptied its flush queue; we can Rflush now */ + unlock(q); + return 1; + } + /* append to slave's flush queue */ + fq->next = nil; + if(q->flush != nil) + q->flusht->next = fq; + else + q->flush = fq; + q->flusht = fq; + if(q->busy){ + pid = q->slave->pid; + swiproc(q->slave, 0); + } + unlock(q); + unlock(fs); + if(exdebug && pid) + print("export: swiproc %ld to flush %d\n", pid, fq->in.oldtag); + return 0; + } + unlock(fs); + + /* not found */ + return 1; +} + +static void +exfreeq(Exq *q) +{ + Exq *fq; + + while((fq = q->flush) != nil){ + q->flush = fq->next; + exfree(fq->export); + free(fq); + } + exfree(q->export); + free(q); +} + +static void +exshutdown(Export *fs) +{ + Exq *q, **last; + + /* work not started */ + lock(&exq.l); + for(last = &exq.head; (q = *last) != nil;) + if(q->export == fs){ + *last = q->next; + exfreeq(q); + }else + last = &q->next; + unlock(&exq.l); + + /* tell slaves to abandon work in progress */ + lock(fs); + while((q = fs->work) != nil){ + fs->work = q->next; + lock(q); + q->shut = 1; + swiproc(q->slave, 0); /* whether busy or not */ + unlock(q); + } + unlock(fs); +} + +static void +exfreefids(Export *fs) +{ + Fid *f, *n; + int i; + + for(i = 0; i < Nfidhash; i++){ + for(f = fs->fid[i]; f != nil; f = n){ + n = f->next; + f->attached = 0; + if(f->ref == 0) { + if(f->chan != nil) + cclose(f->chan); + freeuqid(fs, f->qid); + free(f); + } else + print("exfreefids: busy fid\n"); + } + } +} + +static void +exfree(Export *fs) +{ + if(exdebug) + print("export p/s %ld free %p ref %ld\n", up->pid, fs, fs->r.ref); + if(decref(&fs->r) != 0) + return; + closepgrp(fs->pgrp); + closeegrp(fs->egrp); + closefgrp(fs->fgrp); + cclose(fs->root); + cclose(fs->io); + exfreefids(fs); + free(fs->user); + free(fs); +} + +static int +exwork(void*) +{ + return exq.head != nil; +} + +static void +exslave(void*) +{ + Export *fs; + Exq *q, *t, *fq, **last; + char *err; + + for(;;){ + up->psstate = "Idle"; + qlock(&exq.qwait); + if(waserror()){ + qunlock(&exq.qwait); + continue; + } + sleep(&exq.rwait, exwork, nil); + poperror(); + + lock(&exq.l); + q = exq.head; + if(q == nil) { + unlock(&exq.l); + qunlock(&exq.qwait); + continue; + } + exq.head = q->next; + + 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 + */ + notkilled(); + q->slave = up; + q->busy = 1; /* fcall in progress: interruptible */ + fs = q->export; + lock(fs); + q->next = fs->work; + fs->work = q; + unlock(fs); + unlock(&exq.l); + + up->pgrp = q->export->pgrp; + up->egrp = q->export->egrp; + up->fgrp = q->export->fgrp; + kstrdup(&up->user, q->export->user); + + if(exdebug > 1) + print("exslave %ld dispatch %F\n", up->pid, &q->in); + + if(waserror()){ + print("exslave %ld err %s\n", up->pid, up->errstr); /* shouldn't happen */ + err = up->errstr; + }else{ + if(q->in.type >= Tmax || !fcalls[q->in.type]){ + snprint(up->genbuf, sizeof(up->genbuf), "unknown message: %F", &q->in); + err = up->genbuf; + }else{ + switch(q->in.type){ + case Tread: + q->out.data = (char*)q->buf + IOHDRSZ; + break; + case Tstat: + q->out.stat = q->buf + MSGHDRSZ+STATFIXLEN; + q->out.nstat = sizeof(q->buf)-(MSGHDRSZ+STATFIXLEN); + break; + } + up->psstate = tnames[q->in.type]; + err = (*fcalls[q->in.type])(fs, &q->in, &q->out); + up->psstate = nil; + } + poperror(); + } + + /* + * if the fcall completed without error we must reply, + * even if a flush is pending (because the underlying server + * might have changed state), unless the export has shut down completely. + * must also reply to each flush in order, and only after the original reply (if sent). + */ + lock(q); + notkilled(); + q->busy = 0; /* operation complete */ + if(!q->shut){ + if(q->flush == nil || err == nil){ + unlock(q); + q->out.type = q->in.type+1; + q->out.tag = q->in.tag; + if(err){ + q->out.type = Rerror; + q->out.ename = err; + } + exreply(q, "exslave"); + lock(q); + } + while((fq = q->flush) != nil && !q->shut){ + q->flush = fq->next; + unlock(q); + exreply(fq, "exslave"); + exfreeq(fq); + lock(q); + } + } + q->finished = 1; /* promise not to send any more */ + unlock(q); + + lock(fs); + for(last = &fs->work; (t = *last) != nil; last = &t->next) + if(t == q){ + *last = q->next; + break; + } + unlock(fs); + + notkilled(); + exfreeq(q); + } + print("exslave %ld shut down", up->pid); /* not reached */ + pexit("exslave shut down", 0); +} + +static void +exreply(Exq *q, char *who) +{ + Export *fs; + Fcall *r; + int n; + + fs = q->export; + r = &q->out; + + n = fs->msize; + if(q->in.type == Tversion && r->type == Rerror) + n = MAXRPC; + n = convS2M(r, q->buf, n); + if(n == 0){ + r->type = Rerror; + if(fs->msize == 0) + r->ename = "Tversion not seen"; + else + r->ename = "failed to convert R-message"; + n = convS2M(r, q->buf, MAXRPC); + } + + if(exdebug) + print("%s %ld -> %F\n", who, up->pid, r); + + if(!waserror()){ + devtab[fs->io->type]->write(fs->io, q->buf, n, 0); + poperror(); + } +} + +static int +exiounit(Export *fs, Chan *c) +{ + int iounit; + + iounit = fs->msize-IOHDRSZ; + if(c->iounit != 0 && c->iounit < fs->msize) + iounit = c->iounit; + return iounit; +} + +static Qid +Exrmtqid(Chaninfo *c, Uqid *qid) +{ + Qid q; + + q.path = qid->newpath; + q.vers = c->qid.vers; + q.type = c->qid.type; + return q; +} + +static Fid* +Exmkfid(Export *fs, ulong fid) +{ + ulong h; + Fid *f, *nf; + + nf = malloc(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; + nf->qid = nil; + unlock(&fs->fidlock); + return nf; +} + +static Fid* +Exgetfid(Export *fs, ulong 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; +} + +static void +Exputfid(Export *fs, Fid *f) +{ + Chan *c; + + lock(&fs->fidlock); + f->ref--; + if(f->ref == 0 && f->attached == 0){ + c = f->chan; + f->chan = nil; + *f->last = f->next; + if(f->next != nil) + f->next->last = f->last; + unlock(&fs->fidlock); + if(c != nil) + cclose(c); + freeuqid(fs, f->qid); + free(f); + return; + } + unlock(&fs->fidlock); +} + +static Chan* +exmount(Chan *c, Mhead **mp, int dopath) +{ +/* XXX */ + Chan *nc; + Path *opath; + + nc = nil; + if((c->flag & COPEN) == 0 && findmount(&nc, mp, c->type, c->dev, c->qid)){ + if(waserror()){ + cclose(nc); + nexterror(); + } + nc = cunique(nc); + poperror(); + if(dopath){ + opath = c->path; + incref(opath); + pathclose(nc->path); + nc->path = opath; + } + return nc; + } + incref(c); + return c; +} + +static char* +Exversion(Export *fs, Fcall *t, Fcall *r) +{ + char *p; + static char version[] = VERSION9P; + + r->msize = t->msize; + if(r->msize > MAXRPC) + r->msize = MAXRPC; + if(r->msize < 64) + return "message size too small"; + if((p = strchr(t->version, '.')) != nil) + *p = 0; + if(strncmp(t->version, "9P", 2) ==0 && strcmp(version, t->version) <= 0){ + r->version = version; + fs->msize = r->msize; + }else + r->version = "unknown"; + return nil; +} + +static char* +Exauth(Export *fs, Fcall *t, Fcall *r) +{ + USED(fs); + USED(t); + USED(r); + return "authentication not required"; +} + +static char* +Exattach(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + + f = Exmkfid(fs, t->fid); + if(f == nil) + return Edupfid; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->errstr; + } + f->chan = cclone(fs->root); + f->qid = uqidalloc(fs, f->chan); + poperror(); + r->qid = Exrmtqid(f->chan, f->qid); + Exputfid(fs, f); + return nil; +} + +static char* +Exclunk(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + f->attached = 0; + Exputfid(fs, f); + return nil; +} + +static int +namestowalk(char **name, int nname) +{ + int i; + + for(i=0; ifid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Eopen; + } + + if(waserror()) + return up->errstr; + c = cclone(f->chan); + poperror(); + ninfo = 0; + for(i=0; inwname; i+=n){ + n = namestowalk(t->wname+i, t->nwname-i); + if(n==1 && + (strcmp(t->wname[i], ".") == 0 || + (strcmp(t->wname[i], "..") == 0 && exisroot(fs, c)))){ + if(i > 0 && !(info[i-1].qid.type&QTDIR)) + break; + /* no walk - dot or dotdot in root */ + info[0].qid = c->qid; + info[0].dev = c->dev; + info[0].type = c->type; + ninfo = i+1; + }else{ + if(walkq(&c, t->wname+i, n, 0, nil, info+i, &ninfo) < 0){ + if(i == 0 && ninfo == 0){ + cclose(c); + Exputfid(fs, f); + return up->errstr; + } + ninfo = i+ninfo; + break; + } + } + } + + qid = f->qid; + incref(qid); + r->nwqid = ninfo; + for(i=0; iwqid[i] = Exrmtqid(&info[i], qid); + } + + if(t->newfid != t->fid){ + if(r->nwqid == t->nwname){ + nf = Exmkfid(fs, t->newfid); + if(nf == nil){ + cclose(c); + freeuqid(fs, qid); + Exputfid(fs, f); + return Edupfid; + } + nf->chan = c; + nf->qid = qid; + Exputfid(fs, nf); + } + }else{ + cclose(f->chan); + f->chan = c; + freeuqid(fs, f->qid); + f->qid = qid; + } + Exputfid(fs, f); + return nil; +} + +static char* +Exopen(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + Uqid *qid; + Mhead *m; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Emode; + } + m = nil; + c = exmount(f->chan, &m, 1); + if(waserror()){ + cclose(c); + Exputfid(fs, f); + return up->errstr; + } + + /* only save the mount head if it's a multiple element union */ + if(m && m->mount && m->mount->next) + c->umh = m; + else + putmhead(m); + + c = devtab[c->type]->open(c, t->mode); + if(t->mode & ORCLOSE) + c->flag |= CRCLOSE; + + qid = uqidalloc(fs, c); + poperror(); + freeuqid(fs, f->qid); + cclose(f->chan); + f->chan = c; + f->qid = qid; + f->offset = 0; + r->qid = Exrmtqid(c, f->qid); + r->iounit = exiounit(fs, c); + Exputfid(fs, f); + return nil; +} + +static char* +Excreate(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *cnew, *c; + Uqid *qid; + Mhead *m; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(f->chan->flag & COPEN){ + Exputfid(fs, f); + return Emode; + } + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + validname(t->name, 0); + if(t->name[0] == '.' && (t->name[1] == '\0' || t->name[1] == '.' && t->name[2] == '\0')) + error(Efilename); /* underlying server should check, but stop it here */ + + c = f->chan; + incref(c); + m = nil; + cnew = nil; + if(waserror()){ + if(cnew) + cclose(cnew); + if(m) + putmhead(m); + cclose(c); + nexterror(); + } + + if(findmount(&cnew, &m, c->type, c->dev, c->qid)) + cnew = createdir(cnew, m); + else{ + cnew = c; + incref(cnew); + } + + cnew = cunique(cnew); + pathclose(cnew->path); + cnew->path = c->path; + incref(cnew->path); + + devtab[cnew->type]->create(cnew, t->name, t->mode, t->perm); + + poperror(); + if(m) + putmhead(m); + cclose(c); + c = cnew; + c->path = addelem(c->path, t->name, nil); + + if(t->mode & ORCLOSE) + c->flag |= CRCLOSE; + qid = uqidalloc(fs, c); + poperror(); + + cclose(f->chan); + f->chan = c; + freeuqid(fs, f->qid); + f->qid = qid; + r->qid = Exrmtqid(c, f->qid); + r->iounit = exiounit(fs, c); + Exputfid(fs, f); + return nil; +} + +static char* +Exread(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + long off; + int dir, n, seek; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + + if(waserror()) { + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + if((c->flag & COPEN) == 0) + error(Emode); + if(c->mode != OREAD && c->mode != ORDWR) + error(Eaccess); + if(t->count < 0 || t->count > fs->msize-IOHDRSZ) + error(Ecount); + if(t->offset < 0) + error(Enegoff); + dir = c->qid.type & QTDIR; + if(dir && t->offset != f->offset){ + if(t->offset != 0) + error(Eseekdir); + f->offset = 0; + c->uri = 0; + c->dri = 0; + } + + for(;;){ + n = t->count; + seek = 0; + off = t->offset; + if(dir && f->offset != off){ + off = f->offset; + n = t->offset - off; + if(n > MAXFDATA) + n = MAXFDATA; + seek = 1; + } + if(dir && c->umh != nil){ + if(0) + print("union read %d uri %d dri %d\n", seek, c->uri, c->dri); + n = unionread(c, r->data, n); + } + else{ + c->offset = off; + n = devtab[c->type]->read(c, r->data, n, off); + lock(c); + c->offset += n; + unlock(c); + } + f->offset = off + n; + if(n == 0 || !seek) + break; + } + r->count = n; + + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exwrite(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + c = f->chan; + if((c->flag & COPEN) == 0) + error(Emode); + if(c->mode != OWRITE && c->mode != ORDWR) + error(Eaccess); + if(c->qid.type & QTDIR) + error(Eisdir); + if(t->count < 0 || t->count > fs->msize-IOHDRSZ) + error(Ecount); + if(t->offset < 0) + error(Enegoff); + r->count = devtab[c->type]->write(c, t->data, t->count, t->offset); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exstat(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + int n; + + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + c = exmount(f->chan, nil, 1); + if(waserror()){ + cclose(c); + Exputfid(fs, f); + return up->errstr; + } + n = devtab[c->type]->stat(c, r->stat, r->nstat); + if(n <= BIT16SZ) + error(Eshortstat); + r->nstat = n; + poperror(); + cclose(c); + Exputfid(fs, f); + return nil; +} + +static char* +Exwstat(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + Exputfid(fs, f); + return up->errstr; + } + validstat(t->stat, t->nstat); /* check name */ + + c = exmount(f->chan, nil, 0); + if(waserror()){ + cclose(c); + nexterror(); + } + devtab[c->type]->wstat(c, t->stat, t->nstat); + poperror(); + + cclose(c); + poperror(); + Exputfid(fs, f); + return nil; +} + +static char* +Exremove(Export *fs, Fcall *t, Fcall *r) +{ + Fid *f; + Chan *c; + + USED(r); + f = Exgetfid(fs, t->fid); + if(f == nil) + return Enofid; + if(waserror()){ + f->attached = 0; + Exputfid(fs, f); + return up->errstr; + } + c = exmount(f->chan, nil, 0); + if(waserror()){ + c->type = 0; /* see below */ + cclose(c); + nexterror(); + } + devtab[c->type]->remove(c); + poperror(); + poperror(); + + /* + * chan is already clunked by remove. + * however, we need to recover the chan, + * and follow sysremove's lead in making it point to root. + */ + c->type = 0; + + cclose(c); + f->attached = 0; + Exputfid(fs, f); + return nil; +} + +/* + * unique path generation + */ + +static int +uqidhash(vlong path) +{ + ulong p; + p = (ulong)path; + return ((p>>16) ^ (p>>8) ^ p) & (Nqidhash-1); +} + +static Uqid ** +uqidlook(Uqid **tab, Chaninfo *c, vlong path) +{ + Uqid **hp, *q; + + for(hp = &tab[uqidhash(path)]; (q = *hp) != nil; hp = &q->next) + if(q->type == c->type && q->dev == c->dev && q->oldpath == path) + break; + return hp; +} + +static int +uqidexists(Uqid **tab, vlong path) +{ + int i; + Uqid *q; + + for(i=0; inext) + if(q->newpath == path) + return 1; + return 0; +} + +static Uqid * +uqidalloc(Export *fs, Chaninfo *c) +{ + Uqid **hp, *q; + + qlock(&fs->qidlock); + hp = uqidlook(fs->qids, c, c->qid.path); + if((q = *hp) != nil){ + incref(q); + qunlock(&fs->qidlock); + return q; + } + q = mallocz(sizeof(*q), 1); + if(q == nil){ + qunlock(&fs->qidlock); + error(Enomem); + } + q->ref = 1; + q->type = c->type; + q->dev = c->dev; + q->oldpath = c->qid.path; + q->newpath = c->qid.path; + while(uqidexists(fs->qids, q->newpath)){ + if(++fs->pathgen >= (1<<16)) + fs->pathgen = 1; + q->newpath = ((vlong)fs->pathgen<<48) | (q->newpath & QIDMASK); + } + q->next = nil; + *hp = q; + qunlock(&fs->qidlock); + return q; +} + +static void +freeuqid(Export *fs, Uqid *q) +{ + Uqid **hp; + + if(q == nil) + return; + qlock(&fs->qidlock); + if(decref(q) == 0){ + hp = &fs->qids[uqidhash(q->oldpath)]; + for(; *hp != nil; hp = &(*hp)->next) + if(*hp == q){ + *hp = q->next; + free(q); + break; + } + } + qunlock(&fs->qidlock); +} + +static void +swiproc(Proc *p, int) +{ + int s; + Rendez *r; + + if(p == nil) + return; + + s = splhi(); + lock(&p->rlock); + p->notepending = 1; + r = p->r; + if(r != nil){ + lock(r); + if(r->p == p){ + r->p = nil; + ready(p); + } + unlock(r); + } + unlock(&p->rlock); + splx(s); +} + +static void +notkilled(void) +{ +} + +static void +kwerrstr(char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vsnprint(up->errstr, ERRMAX, fmt, arg); + va_end(arg); +} --- /sys/src/9/port/portdat.h Thu Mar 17 01:52:21 2016 +++ /sys/src/9/port/portdat.h Thu Mar 17 01:52:17 2016 @@ -1,6 +1,7 @@ typedef struct Alarms Alarms; typedef struct Block Block; typedef struct Chan Chan; +typedef struct Chaninfo Chaninfo; typedef struct Cmdbuf Cmdbuf; typedef struct Cmdtab Cmdtab; typedef struct Confmem Confmem; @@ -189,6 +190,13 @@ #define BLEN(s) ((s)->wp - (s)->rp) #define BALLOC(s) ((s)->lim - (s)->base) +struct Chaninfo +{ + Qid qid; + ushort type; + ulong dev; +}; + struct Chan { Ref; /* the Lock in this Ref is also Chan's lock */ @@ -196,11 +204,9 @@ Chan* link; vlong offset; /* in fd */ vlong devoffset; /* in underlying device; see read */ - ushort type; - ulong dev; ushort mode; /* read/write */ ushort flag; - Qid qid; + Chaninfo; int fid; /* for devmnt */ ulong iounit; /* chunk size for i/o; 0==default */ Mhead* umh; /* mount point that derived Chan; used in unionread */ --- /sys/src/9/port/portfns.h Thu Mar 17 01:52:25 2016 +++ /sys/src/9/port/portfns.h Thu Mar 17 01:52:23 2016 @@ -371,6 +371,7 @@ void* vmemchr(void*, int, int); Proc* wakeup(Rendez*); int walk(Chan**, char**, int, int, int*); +int walkq(Chan**, char**, int, int, int*, Chaninfo*, int*); void wlock(RWlock*); void wunlock(RWlock*); void* xalloc(ulong); --- /sys/src/9/port/systab.h Thu Mar 17 01:52:34 2016 +++ /sys/src/9/port/systab.h Thu Mar 17 01:52:32 2016 @@ -54,6 +54,7 @@ Syscall syspwrite; Syscall systsemacquire; Syscall sysnsec; +Syscall sysexport; Syscall sysdeath; Syscall *systab[]={ @@ -109,6 +110,7 @@ [PWRITE] syspwrite, [TSEMACQUIRE] systsemacquire, [NSEC] sysnsec, + [EXPORT] sysexport, }; char *sysctab[]={ @@ -164,6 +166,7 @@ [PWRITE] "Pwrite", [TSEMACQUIRE] "Tsemacquire", [NSEC] "Nsec", + [EXPORT] "Export", }; int nsyscall = (sizeof systab/sizeof systab[0]); --- /sys/src/libc/9syscall/sys.h Thu Mar 17 01:52:37 2016 +++ /sys/src/libc/9syscall/sys.h Thu Mar 17 01:52:35 2016 @@ -49,4 +49,5 @@ #define PREAD 50 #define PWRITE 51 #define TSEMACQUIRE 52 -#define NSEC 53 +#define NSEC 53 +#define EXPORT 54