123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803 |
- #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;
- }
|