diff options
Diffstat (limited to 'src/cmd/auth/factotum/fs.c')
-rw-r--r-- | src/cmd/auth/factotum/fs.c | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/src/cmd/auth/factotum/fs.c b/src/cmd/auth/factotum/fs.c new file mode 100644 index 00000000..f9ad785b --- /dev/null +++ b/src/cmd/auth/factotum/fs.c @@ -0,0 +1,531 @@ +#include "std.h" +#include "dat.h" + +enum +{ + Qroot, + Qfactotum, + Qrpc, + Qkeylist, + Qprotolist, + Qconfirm, + Qlog, + Qctl, + Qneedkey, + Qconv, +}; + +static int qtop; + +Qid +mkqid(int type, int path) +{ + Qid q; + + q.type = type; + q.path = path; + q.vers = 0; + return q; +} + +static struct +{ + char *name; + int qidpath; + ulong perm; +} dirtab[] = { + /* positions of confirm and needkey known below */ + "confirm", Qconfirm, 0600|DMEXCL, + "needkey", Qneedkey, 0600|DMEXCL, + "ctl", Qctl, 0600, + "rpc", Qrpc, 0666, + "proto", Qprotolist, 0444, + "log", Qlog, 0600|DMEXCL, + "conv", Qconv, 0400, +}; + +static void +fillstat(Dir *dir, char *name, int type, int path, ulong perm) +{ + dir->name = estrdup(name); + dir->uid = estrdup(owner); + dir->gid = estrdup(owner); + dir->mode = perm; + dir->length = 0; + dir->qid = mkqid(type, path); + dir->atime = time(0); + dir->mtime = time(0); + dir->muid = estrdup(""); +} + +static int +rootdirgen(int n, Dir *dir, void *v) +{ + USED(v); + + if(n > 0) + return -1; + + fillstat(dir, factname, QTDIR, Qfactotum, DMDIR|0555); + return 0; +} + +static int +fsdirgen(int n, Dir *dir, void *v) +{ + USED(v); + + if(n >= nelem(dirtab)) + return -1; + fillstat(dir, dirtab[n].name, 0, dirtab[n].qidpath, dirtab[n].perm); + return 0; +} + +static char* +fswalk1(Fid *fid, char *name, Qid *qid) +{ + int i; + + switch((int)fid->qid.path){ + default: + return "fswalk1: cannot happen"; + case Qroot: + if(strcmp(name, factname) == 0){ + *qid = mkqid(QTDIR, Qfactotum); + fid->qid = *qid; + return nil; + } + if(strcmp(name, "..") == 0){ + *qid = fid->qid; + return nil; + } + return "not found"; + case Qfactotum: + for(i=0; i<nelem(dirtab); i++) + if(strcmp(name, dirtab[i].name) == 0){ + *qid = mkqid(0, dirtab[i].qidpath); + fid->qid = *qid; + return nil; + } + if(strcmp(name, "..") == 0){ + *qid = mkqid(QTDIR, qtop); + fid->qid = *qid; + return nil; + } + return "not found"; + } +} + +static void +fsstat(Req *r) +{ + int i, path; + + path = r->fid->qid.path; + switch(path){ + case Qroot: + fillstat(&r->d, "/", QTDIR, Qroot, 0555|DMDIR); + break; + case Qfactotum: + fillstat(&r->d, "factotum", QTDIR, Qfactotum, 0555|DMDIR); + break; + default: + for(i=0; i<nelem(dirtab); i++) + if(dirtab[i].qidpath == path){ + fillstat(&r->d, dirtab[i].name, 0, dirtab[i].qidpath, dirtab[i].perm); + goto Break2; + } + respond(r, "file not found"); + break; + } + Break2: + respond(r, nil); +} + +static int +readlist(int off, int (*gen)(int, char*, uint), Req *r) +{ + char *a, *ea; + int n; + + a = r->ofcall.data; + ea = a+r->ifcall.count; + for(;;){ + n = (*gen)(off, a, ea-a); + if(n == 0){ + r->ofcall.count = a - (char*)r->ofcall.data; + return off; + } + a += n; + off++; + } + return -1; /* not reached */ +} + +static int +keylist(int i, char *a, uint nn) +{ + int n; + char buf[512]; + Key *k; + + if(i >= ring.nkey) + return 0; + + k = ring.key[i]; + k->attr = sortattr(k->attr); + n = snprint(buf, sizeof buf, "key %A %N\n", k->attr, k->privattr); + if(n >= sizeof(buf)-5) + strcpy(buf+sizeof(buf)-5, "...\n"); + n = strlen(buf); + if(n > nn) + return 0; + memmove(a, buf, n); + return n; +} + +static int +protolist(int i, char *a, uint n) +{ + if(prototab[i] == nil) + return 0; + if(strlen(prototab[i]->name)+1 > n) + return 0; + n = strlen(prototab[i]->name)+1; + memmove(a, prototab[i]->name, n-1); + a[n-1] = '\n'; + return n; +} + +/* BUG this is O(n^2) to fill in the list */ +static int +convlist(int i, char *a, uint nn) +{ + Conv *c; + char buf[512]; + int n; + + for(c=conv; c && i-- > 0; c=c->next) + ; + + if(c == nil) + return 0; + + if(c->state) + n = snprint(buf, sizeof buf, "conv state=%q %A\n", c->state, c->attr); + else + n = snprint(buf, sizeof buf, "conv state=closed err=%q\n", c->err); + + if(n >= sizeof(buf)-5) + strcpy(buf+sizeof(buf)-5, "...\n"); + n = strlen(buf); + if(n > nn) + return 0; + memmove(a, buf, n); + return n; +} + +static void +fskickreply(Conv *c) +{ + Req *r; + + if(c->hangup){ + if(c->req){ + respond(c->req, "hangup"); + c->req = nil; + } + return; + } + + if(!c->req || !c->nreply) + return; + + r = c->req; + r->ofcall.count = c->nreply; + r->ofcall.data = c->reply; + if(r->ofcall.count > r->ifcall.count) + r->ofcall.count = r->ifcall.count; + respond(r, nil); + c->req = nil; + c->nreply = 0; +} + +/* + * Some of the file system work happens in the fs proc, but + * fsopen, fsread, fswrite, fsdestroyfid, and fsflush happen in + * the main proc so that they can access the various shared + * data structures without worrying about locking. + */ +static int inuse[nelem(dirtab)]; +int *confirminuse = &inuse[0]; +int *needkeyinuse = &inuse[1]; +static void +fsopen(Req *r) +{ + int i, *inusep, perm; + static int need[4] = { 4, 2, 6, 1 }; + Conv *c; + + inusep = nil; + perm = 5; /* directory */ + for(i=0; i<nelem(dirtab); i++) + if(dirtab[i].qidpath == r->fid->qid.path){ + if(dirtab[i].perm & DMEXCL) + inusep = &inuse[i]; + if(strcmp(r->fid->uid, owner) == 0) + perm = dirtab[i].perm>>6; + else + perm = dirtab[i].perm; + break; + } + + if((r->ifcall.mode&~(OMASK|OTRUNC)) + || (need[r->ifcall.mode&3] & ~perm)){ + respond(r, "permission denied"); + return; + } + + if(inusep){ + if(*inusep){ + respond(r, "file in use"); + return; + } + *inusep = 1; + } + + if(r->fid->qid.path == Qrpc){ + if((c = convalloc(r->fid->uid)) == nil){ + char e[ERRMAX]; + + rerrstr(e, sizeof e); + respond(r, e); + return; + } + c->kickreply = fskickreply; + r->fid->aux = c; + } + + respond(r, nil); +} + +static void +fsread(Req *r) +{ + Conv *c; + + switch((int)r->fid->qid.path){ + default: + respond(r, "fsread: cannot happen"); + break; + case Qroot: + dirread9p(r, rootdirgen, nil); + respond(r, nil); + break; + case Qfactotum: + dirread9p(r, fsdirgen, nil); + respond(r, nil); + break; + case Qrpc: + c = r->fid->aux; + if(c->rpc.op == RpcUnknown){ + respond(r, "no rpc pending"); + break; + } + if(c->req){ + respond(r, "read already pending"); + break; + } + c->req = r; + if(c->nreply) + (*c->kickreply)(c); + else + rpcexec(c); + break; + case Qconfirm: + confirmread(r); + break; + case Qlog: + logread(r); + break; + case Qctl: + r->fid->aux = (void*)readlist((int)r->fid->aux, keylist, r); + respond(r, nil); + break; + case Qneedkey: + needkeyread(r); + break; + case Qprotolist: + r->fid->aux = (void*)readlist((int)r->fid->aux, protolist, r); + respond(r, nil); + break; + case Qconv: + r->fid->aux = (void*)readlist((int)r->fid->aux, convlist, r); + respond(r, nil); + break; + } +} + +static void +fswrite(Req *r) +{ + int ret; + char err[ERRMAX], *s; + int (*strfn)(char*); + + switch((int)r->fid->qid.path){ + default: + respond(r, "fswrite: cannot happen"); + break; + case Qrpc: + if(rpcwrite(r->fid->aux, r->ifcall.data, r->ifcall.count) < 0){ + rerrstr(err, sizeof err); + respond(r, err); + }else{ + r->ofcall.count = r->ifcall.count; + respond(r, nil); + } + break; + case Qneedkey: + strfn = needkeywrite; + goto string; + case Qctl: + strfn = ctlwrite; + goto string; + case Qconfirm: + strfn = confirmwrite; + string: + s = emalloc(r->ifcall.count+1); + memmove(s, r->ifcall.data, r->ifcall.count); + s[r->ifcall.count] = '\0'; + ret = (*strfn)(s); + free(s); + if(ret < 0){ + rerrstr(err, sizeof err); + respond(r, err); + }else{ + r->ofcall.count = r->ifcall.count; + respond(r, nil); + } + break; + } +} + +static void +fsflush(Req *r) +{ + confirmflush(r); + logflush(r); +} + +static void +fsdestroyfid(Fid *fid) +{ + if(fid->qid.path == Qrpc && fid->aux){ + convhangup(fid->aux); + convclose(fid->aux); + } +} + +static Channel *creq; +static Channel *cfid, *cfidr; + +static void +fsreqthread(void *v) +{ + Req *r; + + USED(v); + + while((r = recvp(creq)) != nil){ + switch(r->ifcall.type){ + default: + respond(r, "bug in fsreqthread"); + break; + case Topen: + fsopen(r); + break; + case Tread: + fsread(r); + break; + case Twrite: + fswrite(r); + break; + case Tflush: + fsflush(r); + break; + } + } +} + +static void +fsclunkthread(void *v) +{ + Fid *f; + + USED(v); + + while((f = recvp(cfid)) != nil){ + fsdestroyfid(f); + sendp(cfidr, 0); + } +} + +static void +fsproc(void *v) +{ + USED(v); + + threadcreate(fsreqthread, nil, STACK); + threadcreate(fsclunkthread, nil, STACK); + threadexits(nil); +} + +static void +fsattach(Req *r) +{ + r->fid->qid = mkqid(QTDIR, qtop); + r->ofcall.qid = r->fid->qid; + respond(r, nil); +} + +static void +fssend(Req *r) +{ + sendp(creq, r); +} + +static void +fssendclunk(Fid *f) +{ + sendp(cfid, f); + recvp(cfidr); +} + +void +fsstart(Srv *s) +{ + USED(s); + + if(extrafactotumdir) + qtop = Qroot; + else + qtop = Qfactotum; + creq = chancreate(sizeof(Req*), 0); + cfid = chancreate(sizeof(Fid*), 0); + cfidr = chancreate(sizeof(Fid*), 0); + proccreate(fsproc, nil, STACK); +} + +Srv fs = { +.attach= fsattach, +.walk1= fswalk1, +.open= fssend, +.read= fssend, +.write= fssend, +.stat= fsstat, +.flush= fssend, +.destroyfid= fssendclunk, +.start= fsstart, +}; + |