#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "../port/error.h"#include "kernel.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, MAXRPCDEF = IOHDRSZ+MAXFDATA, /* initial/default */ MAXRPCMAX = IOHDRSZ+64*1024, /* most every allowed */ 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; uchar* buf; int bsize;}; 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*, Chan*);static void freeuqid(Export*, Uqid*); 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 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; intexport(int fd, char *dir, int async){ Chan *c, *dc; Pgrp *pg; Egrp *eg; Export *fs; if(waserror()) return -1; c = fdtochan(up->env->fgrp, fd, ORDWR, 1, 1); poperror(); if(waserror()){ cclose(c); return -1; } dc = namec(dir, Atodir, 0, 0); poperror(); fs = malloc(sizeof(Export)); if(fs == nil){ cclose(c); cclose(dc); error(Enomem); } fs->r.ref = 1; pg = up->env->pgrp; fs->pgrp = pg; incref(pg); eg = up->env->egrp; fs->egrp = eg; if(eg != nil) incref(eg); fs->fgrp = newfgrp(nil); kstrdup(&fs->user, up->env->user); fs->root = dc; fs->io = c; fs->pathgen = 0; fs->msize = 0; c->flag |= CMSG; fs->async = async; if(async){ if(waserror()) return -1; kproc("exportfs", exportproc, fs, 0); poperror(); }else exportproc(fs); return 0;} static voidexportinit(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; unlock(&exq.l);} static intexisroot(Export *fs, Chan *c){ return eqchan(fs->root, c, 1);} static intexreadn(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 intexreadmsg(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 Styx 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 voidexportproc(void *a){ Exq *q; int async, msize; int n, type; Export *fs = a; exportinit(); for(;;){ msize = fs->msize; if(msize == 0) msize = MAXRPCDEF; for(n=0;; n++){ /* we don't use smalloc, to avoid memset */ q = mallocz(sizeof(*q)+msize, 0); if(q != nil || n > 6000) break; if(n%600 == 0) print("exportproc %ld: waiting for memory (%d) for request\n", up->pid, msize); tsleep(&up->sleep, return0, nil, 100); } if(q == nil){ kwerrstr("out of memory: read request"); n = -1; break; } memset(q, 0, sizeof(*q)); q->buf = (uchar*)q + sizeof(*q); q->bsize = msize; n = exreadmsg(fs->io, q->buf, msize); /* TO DO: avoid copy */ if(n <= 0) break; if(convM2S(q->buf, n, &q->in) != n){ 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, 0); wakeup(&exq.rwait); } if(exdebug){ if(n < 0) print("exportproc %ld shut down: %s\n", up->pid, up->env->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 intexflushed(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 voidexfreeq(Exq *q){ Exq *fq; while((fq = q->flush) != nil){ q->flush = fq->next; exfree(fq->export); free(fq); } exfree(q->export); free(q);} static voidexshutdown(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 voidexfreefids(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 voidexfree(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);}Lines 1–500 of 1330. Load more
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "kernel.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, MAXRPCDEF = IOHDRSZ+MAXFDATA, /* initial/default */ MAXRPCMAX = IOHDRSZ+64*1024, /* most every allowed */ 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; uchar* buf; int bsize; }; 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*, Chan*); static void freeuqid(Export*, Uqid*); 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 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; int export(int fd, char *dir, int async) { Chan *c, *dc; Pgrp *pg; Egrp *eg; Export *fs; if(waserror()) return -1; c = fdtochan(up->env->fgrp, fd, ORDWR, 1, 1); poperror(); if(waserror()){ cclose(c); return -1; } dc = namec(dir, Atodir, 0, 0); poperror(); fs = malloc(sizeof(Export)); if(fs == nil){ cclose(c); cclose(dc); error(Enomem); } fs->r.ref = 1; pg = up->env->pgrp; fs->pgrp = pg; incref(pg); eg = up->env->egrp; fs->egrp = eg; if(eg != nil) incref(eg); fs->fgrp = newfgrp(nil); kstrdup(&fs->user, up->env->user); fs->root = dc; fs->io = c; fs->pathgen = 0; fs->msize = 0; c->flag |= CMSG; fs->async = async; if(async){ if(waserror()) return -1; kproc("exportfs", exportproc, fs, 0); poperror(); }else 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; 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 Styx 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, msize; int n, type; Export *fs = a; exportinit(); for(;;){ msize = fs->msize; if(msize == 0) msize = MAXRPCDEF; for(n=0;; n++){ /* we don't use smalloc, to avoid memset */ q = mallocz(sizeof(*q)+msize, 0); if(q != nil || n > 6000) break; if(n%600 == 0) print("exportproc %ld: waiting for memory (%d) for request\n", up->pid, msize); tsleep(&up->sleep, return0, nil, 100); } if(q == nil){ kwerrstr("out of memory: read request"); n = -1; break; } memset(q, 0, sizeof(*q)); q->buf = (uchar*)q + sizeof(*q); q->bsize = msize; n = exreadmsg(fs->io, q->buf, msize); /* TO DO: avoid copy */ if(n <= 0) break; if(convM2S(q->buf, n, &q->in) != n){ 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, 0); wakeup(&exq.rwait); } if(exdebug){ if(n < 0) print("exportproc %ld shut down: %s\n", up->pid, up->env->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); }