#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, 0644, "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; iqid = *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; id, 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++; } /* not reached */ } static int keylist(int i, char *a, uint nn) { int n; char buf[4096]; 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((r = c->req) != nil){ c->req = nil; respond(r, "hangup"); } 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; c->req = nil; respond(r, 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; ifid->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*)(uintptr)readlist((uintptr)r->fid->aux, keylist, r); respond(r, nil); break; case Qneedkey: needkeyread(r); break; case Qprotolist: r->fid->aux = (void*)(uintptr)readlist((uintptr)r->fid->aux, protolist, r); respond(r, nil); break; case Qconv: r->fid->aux = (void*)(uintptr)readlist((uintptr)r->fid->aux, convlist, r); respond(r, nil); break; } } static void fswrite(Req *r) { int ret; char err[ERRMAX], *s; int (*strfn)(char*); char *name; 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: name = "needkey"; strfn = needkeywrite; goto string; case Qctl: name = "ctl"; strfn = ctlwrite; goto string; case Qconfirm: name = "confirm"; 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); flog("write %s: %s", name, err); }else{ r->ofcall.count = r->ifcall.count; respond(r, nil); } break; } } static void fsflush(Req *r) { confirmflush(r->oldreq); logflush(r->oldreq); respond(r, nil); } 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, 4*1024 /*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); } void fsend(Srv*) { /* first approximation. just quit now. don't wait for convs to finish */ threadkillgrp(threadgetgrp()); } Srv fs; void fsinit0(void) { fs.attach = fsattach; fs.walk1 = fswalk1; fs.open = fssend; fs.read = fssend; fs.write = fssend; fs.stat = fsstat; fs.flush = fssend; fs.destroyfid = fssendclunk; fs.start = fsstart; fs.end = fsend; }