aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/auth/factotum/fs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/auth/factotum/fs.c')
-rw-r--r--src/cmd/auth/factotum/fs.c531
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,
+};
+