diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/wikifs/fs.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/wikifs/fs.c')
-rwxr-xr-x | sys/src/cmd/wikifs/fs.c | 880 |
1 files changed, 880 insertions, 0 deletions
diff --git a/sys/src/cmd/wikifs/fs.c b/sys/src/cmd/wikifs/fs.c new file mode 100755 index 000000000..d67a2c0de --- /dev/null +++ b/sys/src/cmd/wikifs/fs.c @@ -0,0 +1,880 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <String.h> +#include <thread.h> +#include "wiki.h" + +#include <auth.h> +#include <fcall.h> +#include <9p.h> + +enum { + Qindexhtml, + Qindextxt, + Qraw, + Qhistoryhtml, + Qhistorytxt, + Qdiffhtml, + Qedithtml, + Qwerrorhtml, + Qwerrortxt, + Qhttplogin, + Nfile, +}; + +static char *filelist[] = { + "index.html", + "index.txt", + "current", + "history.html", + "history.txt", + "diff.html", + "edit.html", + "werror.html", + "werror.txt", + ".httplogin", +}; + +static int needhist[Nfile] = { +[Qhistoryhtml] 1, +[Qhistorytxt] 1, +[Qdiffhtml] 1, +}; + +/* + * The qids are <8-bit type><16-bit page number><16-bit page version><8-bit file index>. + */ +enum { /* <8-bit type> */ + Droot = 1, + D1st, + D2nd, + Fnew, + Fmap, + F1st, + F2nd, +}; + +uvlong +mkqid(int type, int num, int vers, int file) +{ + return ((uvlong)type<<40) | ((uvlong)num<<24) | (vers<<8) | file; +} + +int +qidtype(uvlong path) +{ + return (path>>40)&0xFF; +} + +int +qidnum(uvlong path) +{ + return (path>>24)&0xFFFF; +} + +int +qidvers(uvlong path) +{ + return (path>>8)&0xFFFF; +} + +int +qidfile(uvlong path) +{ + return path&0xFF; +} + +typedef struct Aux Aux; +struct Aux { + String *name; + Whist *w; + int n; + ulong t; + String *s; + Map *map; +}; + +static void +fsattach(Req *r) +{ + Aux *a; + + if(r->ifcall.aname && r->ifcall.aname[0]){ + respond(r, "invalid attach specifier"); + return; + } + + a = emalloc(sizeof(Aux)); + r->fid->aux = a; + a->name = s_copy(r->ifcall.uname); + + r->ofcall.qid = (Qid){mkqid(Droot, 0, 0, 0), 0, QTDIR}; + r->fid->qid = r->ofcall.qid; + respond(r, nil); +} + +static String * +httplogin(void) +{ + String *s=s_new(); + Biobuf *b; + + if((b = wBopen(".httplogin", OREAD)) == nil) + goto Return; + + while(s_read(b, s, Bsize) > 0) + ; + Bterm(b); + +Return: + return s; +} + +static char* +fswalk1(Fid *fid, char *name, Qid *qid) +{ + char *q; + int i, isdotdot, n, t; + uvlong path; + Aux *a; + Whist *wh; + String *s; + + isdotdot = strcmp(name, "..")==0; + n = strtoul(name, &q, 10); + path = fid->qid.path; + a = fid->aux; + + switch(qidtype(path)){ + case 0: + return "wikifs: bad path in server (bug)"; + + case Droot: + if(isdotdot){ + *qid = fid->qid; + return nil; + } + if(strcmp(name, "new")==0){ + *qid = (Qid){mkqid(Fnew, 0, 0, 0), 0, 0}; + return nil; + } + if(strcmp(name, "map")==0){ + *qid = (Qid){mkqid(Fmap, 0, 0, 0), 0, 0}; + return nil; + } + if((*q!='\0' || (wh=getcurrent(n))==nil) + && (wh=getcurrentbyname(name))==nil) + return "file does not exist"; + *qid = (Qid){mkqid(D1st, wh->n, 0, 0), wh->doc->time, QTDIR}; + a->w = wh; + return nil; + + case D1st: + if(isdotdot){ + *qid = (Qid){mkqid(Droot, 0, 0, 0), 0, QTDIR}; + return nil; + } + + /* handle history directories */ + if(*q == '\0'){ + if((wh = gethistory(qidnum(path))) == nil) + return "file does not exist"; + for(i=0; i<wh->ndoc; i++) + if(wh->doc[i].time == n) + break; + if(i==wh->ndoc){ + closewhist(wh); + return "file does not exist"; + } + closewhist(a->w); + a->w = wh; + a->n = i; + *qid = (Qid){mkqid(D2nd, qidnum(path), i, 0), wh->doc[i].time, QTDIR}; + return nil; + } + + /* handle files other than index */ + for(i=0; i<nelem(filelist); i++){ + if(strcmp(name, filelist[i])==0){ + if(needhist[i]){ + if((wh = gethistory(qidnum(path))) == nil) + return "file does not exist"; + closewhist(a->w); + a->w = wh; + } + *qid = (Qid){mkqid(F1st, qidnum(path), 0, i), a->w->doc->time, 0}; + goto Gotfile; + } + } + return "file does not exist"; + + case D2nd: + if(isdotdot){ + /* + * Can't use a->w[a->ndoc-1] because that + * might be a failed write rather than the real one. + */ + *qid = (Qid){mkqid(D1st, qidnum(path), 0, 0), 0, QTDIR}; + if((wh = getcurrent(qidnum(path))) == nil) + return "file does not exist"; + closewhist(a->w); + a->w = wh; + a->n = 0; + return nil; + } + for(i=0; i<=Qraw; i++){ + if(strcmp(name, filelist[i])==0){ + *qid = (Qid){mkqid(F2nd, qidnum(path), qidvers(path), i), a->w->doc->time, 0}; + goto Gotfile; + } + } + return "file does not exist"; + + default: + return "bad programming"; + } + /* not reached */ + +Gotfile: + t = qidtype(qid->path); + switch(qidfile(qid->path)){ + case Qindexhtml: + s = tohtml(a->w, a->w->doc+a->n, + t==F1st? Tpage : Toldpage); + break; + case Qindextxt: + s = totext(a->w, a->w->doc+a->n, + t==F1st? Tpage : Toldpage); + break; + case Qraw: + s = s_copy(a->w->title); + s = s_append(s, "\n"); + s = doctext(s, &a->w->doc[a->n]); + break; + case Qhistoryhtml: + s = tohtml(a->w, a->w->doc+a->n, Thistory); + break; + case Qhistorytxt: + s = totext(a->w, a->w->doc+a->n, Thistory); + break; + case Qdiffhtml: + s = tohtml(a->w, a->w->doc+a->n, Tdiff); + break; + case Qedithtml: + s = tohtml(a->w, a->w->doc+a->n, Tedit); + break; + case Qwerrorhtml: + s = tohtml(a->w, a->w->doc+a->n, Twerror); + break; + case Qwerrortxt: + s = totext(a->w, a->w->doc+a->n, Twerror); + break; + case Qhttplogin: + s = httplogin(); + break; + default: + return "internal error"; + } + a->s = s; + return nil; +} + +static void +fsopen(Req *r) +{ + int t; + uvlong path; + Aux *a; + Fid *fid; + Whist *wh; + + fid = r->fid; + path = fid->qid.path; + t = qidtype(fid->qid.path); + if((r->ifcall.mode != OREAD && t != Fnew && t != Fmap) + || (r->ifcall.mode&ORCLOSE)){ + respond(r, "permission denied"); + return; + } + + a = fid->aux; + switch(t){ + case Droot: + currentmap(0); + rlock(&maplock); + a->map = map; + incref(map); + runlock(&maplock); + respond(r, nil); + break; + + case D1st: + if((wh = gethistory(qidnum(path))) == nil){ + respond(r, "file does not exist"); + return; + } + closewhist(a->w); + a->w = wh; + a->n = a->w->ndoc-1; + r->ofcall.qid.vers = wh->doc[a->n].time; + r->fid->qid = r->ofcall.qid; + respond(r, nil); + break; + + case D2nd: + respond(r, nil); + break; + + case Fnew: + a->s = s_copy(""); + respond(r, nil); + break; + + case Fmap: + case F1st: + case F2nd: + respond(r, nil); + break; + + default: + respond(r, "programmer error"); + break; + } +} + +static char* +fsclone(Fid *old, Fid *new) +{ + Aux *a; + + a = emalloc(sizeof(*a)); + *a = *(Aux*)old->aux; + if(a->s) + s_incref(a->s); + if(a->w) + incref(a->w); + if(a->map) + incref(a->map); + if(a->name) + s_incref(a->name); + new->aux = a; + new->qid = old->qid; + + return nil; +} + +static void +fsdestroyfid(Fid *fid) +{ + Aux *a; + + a = fid->aux; + if(a==nil) + return; + + if(a->name) + s_free(a->name); + if(a->map) + closemap(a->map); + if(a->s) + s_free(a->s); + if(a->w) + closewhist(a->w); + free(a); + fid->aux = nil; +} + +static void +fillstat(Dir *d, uvlong path, ulong tm, ulong length) +{ + char tmp[32], *p; + int type; + + memset(d, 0, sizeof(Dir)); + d->uid = estrdup9p("wiki"); + d->gid = estrdup9p("wiki"); + + switch(qidtype(path)){ + case Droot: + case D1st: + case D2nd: + type = QTDIR; + break; + default: + type = 0; + break; + } + d->qid = (Qid){path, tm, type}; + + d->atime = d->mtime = tm; + d->length = length; + if(qidfile(path) == Qedithtml) + d->atime = d->mtime = time(0); + + switch(qidtype(path)){ + case Droot: + d->name = estrdup("/"); + d->mode = DMDIR|0555; + break; + + case D1st: + d->name = numtoname(qidnum(path)); + if(d->name == nil) + d->name = estrdup("<dead>"); + for(p=d->name; *p; p++) + if(*p==' ') + *p = '_'; + d->mode = DMDIR|0555; + break; + + case D2nd: + snprint(tmp, sizeof tmp, "%lud", tm); + d->name = estrdup(tmp); + d->mode = DMDIR|0555; + break; + + case Fmap: + d->name = estrdup("map"); + d->mode = 0666; + break; + + case Fnew: + d->name = estrdup("new"); + d->mode = 0666; + break; + + case F1st: + d->name = estrdup(filelist[qidfile(path)]); + d->mode = 0444; + break; + + case F2nd: + d->name = estrdup(filelist[qidfile(path)]); + d->mode = 0444; + break; + + default: + print("bad qid path 0x%.8llux\n", path); + break; + } +} + +static void +fsstat(Req *r) +{ + Aux *a; + Fid *fid; + ulong t; + + t = 0; + fid = r->fid; + if((a = fid->aux) && a->w) + t = a->w->doc[a->n].time; + + fillstat(&r->d, fid->qid.path, t, a->s ? s_len(a->s) : 0); + respond(r, nil); +} + +typedef struct Bogus Bogus; +struct Bogus { + uvlong path; + Aux *a; +}; + +static int +rootgen(int i, Dir *d, void *aux) +{ + Aux *a; + Bogus *b; + + b = aux; + a = b->a; + switch(i){ + case 0: /* new */ + fillstat(d, mkqid(Fnew, 0, 0, 0), a->map->t, 0); + return 0; + case 1: /* map */ + fillstat(d, mkqid(Fmap, 0, 0, 0), a->map->t, 0); + return 0; + default: /* first-level directory */ + i -= 2; + if(i >= a->map->nel) + return -1; + fillstat(d, mkqid(D1st, a->map->el[i].n, 0, 0), a->map->t, 0); + return 0; + } +} + +static int +firstgen(int i, Dir *d, void *aux) +{ + ulong t; + Bogus *b; + int num; + Aux *a; + + b = aux; + num = qidnum(b->path); + a = b->a; + t = a->w->doc[a->n].time; + + if(i < Nfile){ /* file in first-level directory */ + fillstat(d, mkqid(F1st, num, 0, i), t, 0); + return 0; + } + i -= Nfile; + + if(i < a->w->ndoc){ /* second-level (history) directory */ + fillstat(d, mkqid(D2nd, num, i, 0), a->w->doc[i].time, 0); + return 0; + } + //i -= a->w->ndoc; + + return -1; +} + +static int +secondgen(int i, Dir *d, void *aux) +{ + Bogus *b; + uvlong path; + Aux *a; + + b = aux; + path = b->path; + a = b->a; + + if(i <= Qraw){ /* index.html, index.txt, raw */ + fillstat(d, mkqid(F2nd, qidnum(path), qidvers(path), i), a->w->doc[a->n].time, 0); + return 0; + } + //i -= Qraw; + + return -1; +} + +static void +fsread(Req *r) +{ + char *t, *s; + uvlong path; + Aux *a; + Bogus b; + + a = r->fid->aux; + path = r->fid->qid.path; + b.a = a; + b.path = path; + switch(qidtype(path)){ + default: + respond(r, "cannot happen (bad qid)"); + return; + + case Droot: + if(a == nil || a->map == nil){ + respond(r, "cannot happen (no map)"); + return; + } + dirread9p(r, rootgen, &b); + respond(r, nil); + return; + + case D1st: + if(a == nil || a->w == nil){ + respond(r, "cannot happen (no wh)"); + return; + } + dirread9p(r, firstgen, &b); + respond(r, nil); + return; + + case D2nd: + dirread9p(r, secondgen, &b); + respond(r, nil); + return; + + case Fnew: + if(a->s){ + respond(r, "protocol botch"); + return; + } + /* fall through */ + case Fmap: + t = numtoname(a->n); + if(t == nil){ + respond(r, "unknown name"); + return; + } + for(s=t; *s; s++) + if(*s == ' ') + *s = '_'; + readstr(r, t); + free(t); + respond(r, nil); + return; + + case F1st: + case F2nd: + if(a == nil || a->s == nil){ + respond(r, "cannot happen (no s)"); + return; + } + readbuf(r, s_to_c(a->s), s_len(a->s)); + respond(r, nil); + return; + } +} + +typedef struct Sread Sread; +struct Sread { + char *rp; +}; + +static char* +Srdline(void *v, int c) +{ + char *p, *rv; + Sread *s; + + s = v; + if(s->rp == nil) + rv = nil; + else if(p = strchr(s->rp, c)){ + *p = '\0'; + rv = s->rp; + s->rp = p+1; + }else{ + rv = s->rp; + s->rp = nil; + } + return rv; +} + +static void +responderrstr(Req *r) +{ + char buf[ERRMAX]; + + rerrstr(buf, sizeof buf); + if(buf[0] == '\0') + strcpy(buf, "unknown error"); + respond(r, buf); +} + +static void +fswrite(Req *r) +{ + char *author, *comment, *net, *err, *p, *title, tmp[40]; + int rv, n; + ulong t; + Aux *a; + Fid *fid; + Sread s; + String *stmp; + Whist *w; + + fid = r->fid; + a = fid->aux; + switch(qidtype(fid->qid.path)){ + case Fmap: + stmp = s_nappend(s_reset(nil), r->ifcall.data, r->ifcall.count); + a->n = nametonum(s_to_c(stmp)); + s_free(stmp); + if(a->n < 0) + respond(r, "name not found"); + else + respond(r, nil); + return; + case Fnew: + break; + default: + respond(r, "cannot happen"); + return; + } + + if(a->s == nil){ + respond(r, "protocol botch"); + return; + } + if(r->ifcall.count==0){ /* do final processing */ + s.rp = s_to_c(a->s); + w = nil; + err = "bad format"; + if((title = Srdline(&s, '\n')) == nil){ + Error: + if(w) + closewhist(w); + s_free(a->s); + a->s = nil; + respond(r, err); + return; + } + + w = emalloc(sizeof(*w)); + incref(w); + w->title = estrdup(title); + + t = 0; + author = estrdup(s_to_c(a->name)); + + comment = nil; + while(s.rp && *s.rp && *s.rp != '\n'){ + p = Srdline(&s, '\n'); + assert(p != nil); + switch(p[0]){ + case 'A': + free(author); + author = estrdup(p+1); + break; + case 'D': + t = strtoul(p+1, &p, 10); + if(*p != '\0') + goto Error; + break; + case 'C': + free(comment); + comment = estrdup(p+1); + break; + } + } + + w->doc = emalloc(sizeof(w->doc[0])); + w->doc->time = time(0); + w->doc->comment = comment; + + if(net = r->pool->srv->aux){ + p = emalloc(strlen(author)+10+strlen(net)); + strcpy(p, author); + strcat(p, " ("); + strcat(p, net); + strcat(p, ")"); + free(author); + author = p; + } + w->doc->author = author; + + if((w->doc->wtxt = Brdpage(Srdline, &s)) == nil){ + err = "empty document"; + goto Error; + } + + w->ndoc = 1; + if((n = allocnum(w->title, 0)) < 0) + goto Error; + sprint(tmp, "D%lud\n", w->doc->time); + a->s = s_reset(a->s); + a->s = doctext(a->s, w->doc); + rv = writepage(n, t, a->s, w->title); + s_free(a->s); + a->s = nil; + a->n = n; + closewhist(w); + if(rv < 0) + responderrstr(r); + else + respond(r, nil); + return; + } + + if(s_len(a->s)+r->ifcall.count > Maxfile){ + respond(r, "file too large"); + s_free(a->s); + a->s = nil; + return; + } + a->s = s_nappend(a->s, r->ifcall.data, r->ifcall.count); + r->ofcall.count = r->ifcall.count; + respond(r, nil); +} + +Srv wikisrv = { +.attach= fsattach, +.destroyfid= fsdestroyfid, +.clone= fsclone, +.walk1= fswalk1, +.open= fsopen, +.read= fsread, +.write= fswrite, +.stat= fsstat, +}; + +void +usage(void) +{ + fprint(2, "usage: wikifs [-D] [-a addr]... [-m mtpt] [-p perm] [-s service] dir\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + char **addr; + int i, naddr; + char *buf; + char *service, *mtpt; + ulong perm; + Dir d, *dp; + Srv *s; + + naddr = 0; + addr = nil; + perm = 0; + service = nil; + mtpt = "/mnt/wiki"; + ARGBEGIN{ + case 'D': + chatty9p++; + break; + case 'a': + if(naddr%8 == 0) + addr = erealloc(addr, (naddr+8)*sizeof(addr[0])); + addr[naddr++] = EARGF(usage()); + break; + case 'm': + mtpt = EARGF(usage()); + break; + case 'M': + mtpt = nil; + break; + case 'p': + perm = strtoul(EARGF(usage()), nil, 8); + break; + case 's': + service = EARGF(usage()); + break; + default: + usage(); + break; + }ARGEND + + if(argc != 1) + usage(); + + if((dp = dirstat(argv[0])) == nil) + sysfatal("dirstat %s: %r", argv[0]); + if((dp->mode&DMDIR) == 0) + sysfatal("%s: not a directory", argv[0]); + free(dp); + wikidir = argv[0]; + + currentmap(0); + + for(i=0; i<naddr; i++) + listensrv(&wikisrv, addr[i]); + + s = emalloc(sizeof *s); + *s = wikisrv; + postmountsrv(s, service, mtpt, MREPL|MCREATE); + if(perm){ + buf = emalloc9p(5+strlen(service)+1); + strcpy(buf, "/srv/"); + strcat(buf, service); + nulldir(&d); + d.mode = perm; + if(dirwstat(buf, &d) < 0) + fprint(2, "wstat: %r\n"); + free(buf); + } + exits(nil); +} |