#include #include #include #include #include #include "9p1.h" char *user; int newfd; int roldfd; int woldfd; int debug; int dofcall; QLock servelock; QLock fidlock; QLock taglock; int mainpid; int ntag; int nfork; char FLUSHED[] = "FLUSHED"; enum{ Maxfdata = 8192 }; enum{ Command, Network, File, Stdio, }; typedef struct Tag Tag; struct Tag { int tag; int flushed; int received; int ref; Tag *next; }; typedef struct Message Message; struct Message { char *data; int n; }; typedef struct Fid Fid; struct Fid { short busy; short allocated; int fid; Qid qid; ulong newoffset; ulong oldoffset; Fid *next; }; Fid *fids; Tag *tags; char *rflush(Fcall*, Fcall*, char*), *rversion(Fcall*, Fcall*, char*), *rauth(Fcall*, Fcall*, char*), *rattach(Fcall*, Fcall*, char*), *rwalk(Fcall*, Fcall*, char*), *ropen(Fcall*, Fcall*, char*), *rcreate(Fcall*, Fcall*, char*), *rread(Fcall*, Fcall*, char*), *rwrite(Fcall*, Fcall*, char*), *rclunk(Fcall*, Fcall*, char*), *rremove(Fcall*, Fcall*, char*), *rstat(Fcall*, Fcall*, char*), *rwstat(Fcall*, Fcall*, char*); char *(*fcalls[])(Fcall*, Fcall*, char*) = { [Tversion] rversion, [Tflush] rflush, [Tauth] rauth, [Tattach] rattach, [Twalk] rwalk, [Topen] ropen, [Tcreate] rcreate, [Tread] rread, [Twrite] rwrite, [Tclunk] rclunk, [Tremove] rremove, [Tstat] rstat, [Twstat] rwstat, }; char Etoolong[] = "name too long"; void connect(int, char*); void post(int, char*); void serve(void); void demux(void); void* emalloc(ulong); char* transact9p1(Fcall9p1*, Fcall9p1*, char*); Fid* newfid(int); struct { char chal[CHALLEN]; /* my challenge */ char rchal[CHALLEN]; /* his challenge */ char authid[NAMEREC]; char authdom[DOMLEN]; int id; } ai; void usage(void) { fprint(2, "usage: srvold9p [-abcCd] [-u user] [-s | [-m mountpoint]] [-x 'command' | -n network-addr | -f file] [-F] [-p servicename]\n"); exits("usage"); } void main(int argc, char *argv[]) { int method; char *oldstring; char *mountpoint, *postname; int mountflag, mountfd; int p[2]; int i; fmtinstall('F', fcallfmt); fmtinstall('G', fcallfmt9p1); fmtinstall('D', dirfmt); user = getuser(); mountpoint = nil; mountflag = 0; postname = nil; oldstring = nil; method = -1; mountfd = -1; ARGBEGIN{ case 'a': mountflag |= MAFTER; break; case 'b': mountflag |= MBEFORE; break; case 'c': mountflag |= MCREATE; break; case 'C': mountflag |= MCACHE; break; case 'd': debug++; break; case 'f': method = File; oldstring = ARGF(); break; case 'F': dofcall++; break; case 'm': mountpoint = EARGF(usage()); break; case 'n': method = Network; oldstring = ARGF(); break; case 'p': postname = ARGF(); if(postname == nil) usage(); break; case 's': method = Stdio; break; case 'u': user = EARGF(usage()); break; case 'x': method = Command; oldstring = ARGF(); break; default: usage(); }ARGEND; if(method == Stdio){ if(mountpoint!=nil || argc!=0) usage(); }else{ if(oldstring == nil || argc != 0 || (mountflag!=0 && mountpoint==nil)) usage(); } rfork(RFNOTEG|RFREND); connect(method, oldstring); if(method == Stdio) newfd = 0; else{ if(pipe(p) < 0) fatal("pipe: %r"); if(postname != nil) post(p[0], postname); mountfd = p[0]; newfd = p[1]; } if(debug) fprint(2, "connected and posted\n"); switch(rfork(RFPROC|RFMEM|RFNAMEG|RFFDG)){ case 0: mainpid = getpid(); /* child does all the work */ if(mountfd >= 0) close(mountfd); switch(rfork(RFPROC|RFMEM|RFFDG)){ case 0: for(i = 0; i < 20; i++) if (i != roldfd) close(i); demux(); return; case -1: fatal("fork error: %r"); break; } for(i = 0; i < 20; i++) if (i != newfd && i != woldfd && (debug == 0 || i != 2)) close(i); serve(); break; case -1: fatal("fork error: %r"); break; default: /* parent mounts if required, then exits */ if(mountpoint){ if(mount(mountfd, -1, mountpoint, mountflag, "") < 0) fatal("can't mount: %r"); } break; } exits(nil); } void connect(int method, char *oldstring) { char *s; char dir[256]; switch(method){ default: roldfd = -1; woldfd = -1; fatal("can't handle method type %d", method); break; case Network: s = netmkaddr(oldstring, 0, "9fs"); roldfd = dial(s, 0, dir, 0); if(roldfd < 0) fatal("dial %s: %r", s); woldfd = roldfd; if(dofcall) roldfd = fcall(woldfd); break; case File: roldfd = open(oldstring, ORDWR); if(roldfd < 0) fatal("can't open %s: %r", oldstring); woldfd = roldfd; if(dofcall) roldfd = fcall(woldfd); break; case Stdio: roldfd = fcall(1); woldfd = 1; break; } } void post(int fd, char *srv) { int f; char buf[128]; snprint(buf, sizeof buf, "/srv/%s", srv); f = create(buf, OWRITE, 0666); if(f < 0) fatal("can't create %s: %r", buf); sprint(buf, "%d", fd); if(write(f, buf, strlen(buf)) != strlen(buf)) fatal("post write: %r"); close(f); } Fid * newfid(int fid) { Fid *f, *ff; ff = 0; qlock(&fidlock); for(f = fids; f; f = f->next) if(f->fid == fid){ f->allocated = 1; qunlock(&fidlock); return f; } else if(!ff && !f->allocated) ff = f; if(ff){ ff->fid = fid; ff->allocated = 1; qunlock(&fidlock); return ff; } f = emalloc(sizeof *f); f->fid = fid; f->next = fids; f->allocated = 1; fids = f; qunlock(&fidlock); return f; } /* * Reads returning 9P1 messages and demultiplexes them. * BUG: assumes one read per message. */ void demux(void) { int m, n; char *data; Fcall9p1 r; Message *msg; Tag *t; for(;;){ data = malloc(IOHDRSZ+Maxfdata); /* no need to clear memory */ if(data == nil) fatal("demux malloc: %r"); m = read(roldfd, data, IOHDRSZ+Maxfdata); if(m <= 0) fatal("read error talking to old system: %r"); n = convM2S9p1(data, &r, m); if(n == 0) fatal("bad conversion receiving from old system"); if(debug) fprint(2, "srvold9p:<=%G\n", &r); qlock(&taglock); for(t=tags; t!=nil; t=t->next) if(t->tag == r.tag){ t->received = 1; break; } qunlock(&taglock); /* * Fcall9p1 tag is used to rendezvous. * Recipient converts message a second time, but that's OK. */ msg = emalloc(sizeof(Message)); msg->data = data; msg->n = n; rendezvous((void*)r.tag, msg); } } Tag* newtag(int tag) { Tag *t; t = emalloc(sizeof(Tag)); t->tag = tag; t->flushed = 0; t->received = 0; t->ref = 1; qlock(&taglock); t->next = tags; tags = t; qunlock(&taglock); return t; } void freetag(Tag *tag) /* called with taglock set */ { Tag *t, *prev; if(tag->ref-- == 1){ prev = nil; for(t=tags; t!=nil; t=t->next){ if(t == tag){ if(prev == nil) tags = t->next; else prev->next = t->next; break; } prev = t; } if(t == nil) sysfatal("freetag"); free(tag); } } void serve(void) { char *err; int n; Fcall thdr; Fcall rhdr; uchar mdata[IOHDRSZ+Maxfdata]; char mdata9p1[IOHDRSZ+Maxfdata]; Tag *tag; for(;;){ qlock(&servelock); for(;;){ n = read9pmsg(newfd, mdata, sizeof mdata); if(n == 0) continue; if(n < 0) break; if(n > 0 && convM2S(mdata, n, &thdr) > 0) break; } if(n>0 && servelock.head==nil) /* no other processes waiting to read */ switch(rfork(RFPROC|RFMEM)){ case 0: /* child starts serving */ continue; break; case -1: fatal("fork error: %r"); break; default: break; } qunlock(&servelock); if(n < 0) fatal(nil); /* exit quietly; remote end has just hung up */ if(debug) fprint(2, "srvold9p:<-%F\n", &thdr); tag = newtag(thdr.tag); if(!fcalls[thdr.type]) err = "bad fcall type"; else err = (*fcalls[thdr.type])(&thdr, &rhdr, mdata9p1); qlock(&taglock); if(tag->flushed){ freetag(tag); qunlock(&taglock); continue; } qunlock(&taglock); if(err){ rhdr.type = Rerror; rhdr.ename = err; }else{ rhdr.type = thdr.type + 1; rhdr.fid = thdr.fid; } rhdr.tag = thdr.tag; if(debug) fprint(2, "srvold9p:->%F\n", &rhdr);/**/ n = convS2M(&rhdr, mdata, sizeof mdata); if(n == 0) fatal("convS2M error on write"); if(write(newfd, mdata, n) != n) fatal("mount write"); qlock(&taglock); freetag(tag); qunlock(&taglock); } } void send9p1(Fcall9p1 *t, char *data) { int m, n; if(debug) fprint(2, "srvold9p:=>%G\n", t); n = convS2M9p1(t, data); if(n == 0) fatal("bad conversion sending to old system"); m = write(woldfd, data, n); if(m != n) fatal("wrote %d to old system; should be %d", m, n); } int recv9p1(Fcall9p1 *r, int tag, char *data) { int n; Message *msg; msg = rendezvous((void*)tag, 0); if(msg == (void*)~0) fatal("rendezvous: %r"); if(msg == nil){ if(debug) fprint(2, "recv flushed\n"); return -1; } /* copy data to local buffer */ memmove(data, msg->data, msg->n); n = convM2S9p1(data, r, msg->n); if(n == 0) fatal("bad conversion receiving from old system"); free(msg->data); free(msg); return 1; } char* transact9p1(Fcall9p1 *t, Fcall9p1 *r, char *mdata9p1) { send9p1(t, mdata9p1); if(recv9p1(r, t->tag, mdata9p1) < 0) return FLUSHED; if(r->type == Rerror9p1) return r->ename; if(r->type != t->type+1) fatal("bad message type; expected %d got %d", t->type+1, r->type); return nil; } char* rflush(Fcall *t, Fcall *, char *mdata9p1) { Fcall9p1 t9, r9; Tag *oldt; t9.type = Tflush9p1; t9.tag = t->tag; t9.oldtag = t->oldtag; qlock(&taglock); for(oldt=tags; oldt!=nil; oldt=oldt->next) if(oldt->tag == t->oldtag){ oldt->flushed = 1; oldt->ref++; break; } qunlock(&taglock); if(oldt == nil){ /* nothing to flush */ if(debug) fprint(2, "no such tag to flush\n"); return 0; } transact9p1(&t9, &r9, mdata9p1); /* can't error */ qlock(&taglock); if(oldt->received == 0){ /* wake up receiver */ if(debug) fprint(2, "wake up receiver\n"); oldt->received = 1; rendezvous((void*)t->oldtag, 0); } freetag(oldt); qunlock(&taglock); return 0; } char* rversion(Fcall *t, Fcall *r, char*) { Fid *f; /* just ack; this one doesn't go to old service */ if(t->msize > IOHDRSZ+Maxfdata) r->msize = IOHDRSZ+Maxfdata; else r->msize = t->msize; if(strncmp(t->version, "9P2000", 6) != 0) return "unknown 9P version"; r->version = "9P2000"; qlock(&fidlock); for(f = fids; f; f = f->next){ f->busy = 0; f->allocated = 0; } qunlock(&fidlock); return 0; } char* rauth(Fcall *, Fcall *, char *) { return "srvold9p: authentication not supported"; } #ifdef asdf void memrandom(void *p, int n) { ulong *lp; uchar *cp; for(lp = p; n >= sizeof(ulong); n -= sizeof(ulong)) *lp++ = fastrand(); for(cp = (uchar*)lp; n > 0; n--) *cp++ = fastrand(); } char* rsession(Fcall *t, Fcall *r, char *mdata9p1) { char *err; Fcall9p1 t9, r9; Fid *f; t9.type = Tsession9p1; t9.tag = t->tag; if(doauth) memrandom(t9.chal, sizeof t9.chal); else memset(t9.chal, 0, sizeof t9.chal); err = transact9p1(&t9, &r9, mdata9p1); if(err) return err; qlock(&fidlock); for(f = fids; f; f = f->next){ f->busy = 0; f->allocated = 0; } qunlock(&fidlock); if(doauth){ memmove(ai.authid, r9.authid, sizeof ai.authid); memmove(ai.authdom, r9.authdom, sizeof ai.authid); memmove(ai.rchal, r9.chal, sizeof ai.rchal); memmove(ai.chal, t9.chal, sizeof ai.chal); r->authid = ai.authid; r->authdom = ai.authdom; r->chal = (uchar*)ai.rchal; r->nchal = CHALLEN; } else { r->authid = ""; r->authdom = ""; r->nchal = 0; r->chal = nil; } return 0; } #endif char* rattach(Fcall *t, Fcall *r, char *mdata9p1) { char *err; Fcall9p1 t9, r9; Fid *f; f = newfid(t->fid); if(f->busy) return "attach: fid in use"; /* no authentication! */ t9.type = Tattach9p1; t9.tag = t->tag; t9.fid = t->fid; strncpy(t9.uname, t->uname, NAMEREC); if(strcmp(user, "none") == 0) strncpy(t9.uname, user, NAMEREC); strncpy(t9.aname, t->aname, NAMEREC); memset(t9.ticket, 0, sizeof t9.ticket); memset(t9.auth, 0, sizeof t9.auth); err = transact9p1(&t9, &r9, mdata9p1); if(err) return err; r->qid.path = r9.qid.path & ~0x80000000; r->qid.vers = r9.qid.version; r->qid.type = QTDIR; f->busy = 1; f->qid = r->qid; return 0; } char* rwalk(Fcall *t, Fcall *r, char *mdata9p1) { char *err; Fcall9p1 t9, r9; int i, fid; Qid *q; Fid *f, *nf; f = newfid(t->fid); if(!f->busy) return "walk: bad fid"; fid = t->fid; nf = nil; if(t->fid != t->newfid){ nf = newfid(t->newfid); if(nf->busy) return "walk: newfid in use"; t9.type = Tclone9p1; t9.tag = t->tag; t9.fid = t->fid; t9.newfid = t->newfid; err = transact9p1(&t9, &r9, mdata9p1); if(err){ nf->busy = 0; nf->allocated = 0; return err; } fid = t->newfid; nf->busy = 1; } err = nil; r->nwqid = 0; for(i=0; inwname && err==nil; i++){ if(i > MAXWELEM) break; t9.type = Twalk9p1; t9.tag = t->tag; t9.fid = fid; strncpy(t9.name, t->wname[i], NAMEREC); err = transact9p1(&t9, &r9, mdata9p1); if(err == FLUSHED){ i = -1; /* guarantee cleanup */ break; } if(err == nil){ q = &r->wqid[r->nwqid++]; q->type = QTFILE; if(r9.qid.path & 0x80000000) q->type = QTDIR; q->vers = r9.qid.version; q->path = r9.qid.path & ~0x80000000; } } if(nf!=nil && (err!=nil || inwname)){ /* clunk the new fid */ t9.type = Tclunk9p1; t9.tag = t->tag; t9.fid = t->newfid; transact9p1(&t9, &r9, mdata9p1); /* ignore more errors */ nf->busy = 0; nf->allocated = 0; } if(i>0 && i==t->nwname && err==nil) f->qid = r->wqid[r->nwqid-1]; if(i > 0) return 0; return err; } char* ropen(Fcall *t, Fcall *r, char *mdata9p1) { char *err; Fcall9p1 t9, r9; Fid *f; f = newfid(t->fid); if(!f->busy) return "open: bad fid"; t9.type = Topen9p1; t9.tag = t->tag; t9.fid = t->fid; t9.mode = t->mode; err = transact9p1(&t9, &r9, mdata9p1); if(err) return err; r->qid.path = r9.qid.path & ~0x80000000; r->qid.vers = r9.qid.version; r->qid.type = QTFILE; if(r9.qid.path & 0x80000000) r->qid.type = QTDIR; f->qid = r->qid; f->newoffset = 0; f->oldoffset = 0; r->iounit = 0; return 0; } char* rcreate(Fcall *t, Fcall *r, char *mdata9p1) { char *err; Fcall9p1 t9, r9; Fid *f; f = newfid(t->fid); if(!f->busy) return "create: bad fid"; t9.type = Tcreate9p1; t9.tag = t->tag; t9.fid = t->fid; if(strlen(t->name)+1 >= NAMEREC) return "file name element too long"; strncpy(t9.name, t->name, NAMEREC); t9.perm = t->perm; t9.mode = t->mode; err = transact9p1(&t9, &r9, mdata9p1); if(err) return err; r->qid.path = r9.qid.path & ~0x80000000; r->qid.vers = r9.qid.version; r->qid.type = QTFILE; if(r9.qid.path & 0x80000000) r->qid.type = QTDIR; if(r9.qid.path & 0x40000000) r->qid.type |= QTAPPEND; if(r9.qid.path & 0x20000000) r->qid.type |= QTEXCL; f->qid = r->qid; r->iounit = 0; return 0; } char* dirrread(Fcall *t, Fcall *r, char *mdata9p1) { char *err; Fcall9p1 t9, r9; Fid *f; int i, ndir, n, count; Dir d; uchar buf[Maxfdata]; char *old; f = newfid(t->fid); if(!f->busy) return "dirread: bad fid"; if(f->newoffset != t->offset) return "seek in directory disallowed"; t9.type = Tread9p1; t9.tag = t->tag; t9.fid = t->fid; t9.offset = f->oldoffset; t9.count = t->count; /* new directories tend to be smaller, so this may overshoot */ err = transact9p1(&t9, &r9, mdata9p1); if(err) return err; ndir = r9.count/DIRREC; old = r9.data; count = 0; for(i=0; it->count) break; old += DIRREC; f->oldoffset += DIRREC; f->newoffset += n; count += n; } memmove(r9.data, buf, count); /* put it back in stable storage */ r->data = r9.data; r->count = count; return 0; } char* rread(Fcall *t, Fcall *r, char *mdata9p1) { char *err; Fcall9p1 t9, r9; Fid *f; f = newfid(t->fid); if(!f->busy) return "read: bad fid"; if(f->qid.type & QTDIR) return dirrread(t, r, mdata9p1); t9.type = Tread9p1; t9.tag = t->tag; t9.fid = t->fid; t9.offset = t->offset; t9.count = t->count; err = transact9p1(&t9, &r9, mdata9p1); if(err) return err; r->count = r9.count; r->data = r9.data; /* points to stable storage */ return 0; } char* rwrite(Fcall *t, Fcall *r, char *mdata9p1) { char *err; Fcall9p1 t9, r9; Fid *f; f = newfid(t->fid); if(!f->busy) return "write: bad fid"; t9.type = Twrite9p1; t9.tag = t->tag; t9.fid = t->fid; t9.offset = t->offset; t9.count = t->count; t9.data = t->data; err = transact9p1(&t9, &r9, mdata9p1); if(err) return err; r->count = r9.count; return 0; } char* rclunk(Fcall *t, Fcall *, char *mdata9p1) { Fcall9p1 t9, r9; Fid *f; f = newfid(t->fid); if(!f->busy) return "clunk: bad fid"; t9.type = Tclunk9p1; t9.tag = t->tag; t9.fid = t->fid; transact9p1(&t9, &r9, mdata9p1); f->busy = 0; f->allocated = 0; /* disregard error */ return 0; } char* rremove(Fcall *t, Fcall*, char *mdata9p1) { char *err; Fcall9p1 t9, r9; Fid *f; f = newfid(t->fid); if(!f->busy) return "remove: bad fid"; t9.type = Tremove9p1; t9.tag = t->tag; t9.fid = t->fid; err = transact9p1(&t9, &r9, mdata9p1); f->busy = 0; f->allocated = 0; return err; } char* rstat(Fcall *t, Fcall *r, char *mdata9p1) { Fcall9p1 t9, r9; char *err; Fid *f; Dir d; uchar buf[256]; /* big enough; there's no long names */ f = newfid(t->fid); if(!f->busy) return "stat: bad fid"; t9.type = Tstat9p1; t9.tag = t->tag; t9.fid = t->fid; err = transact9p1(&t9, &r9, mdata9p1); if(err) return err; if(convM2D9p1(r9.stat, &d) != DIRREC) return "bad conversion in stat"; r->stat = buf; r->nstat = convD2M(&d, buf, sizeof buf); return 0; } int anydefault(Dir *d) { if(d->name[0] == '\0') return 1; if(d->uid[0] == '\0') return 1; if(d->gid[0] == '\0') return 1; if(d->mode == ~0) return 1; if(d->mtime == ~0) return 1; return 0; } char* rwstat(Fcall *t, Fcall *, char *mdata9p1) { Fcall9p1 t9, r9; char strs[DIRREC]; char *err; Fid *f; Dir d, cd; f = newfid(t->fid); if(!f->busy) return "wstat: bad fid"; convM2D(t->stat, t->nstat, &d, strs); cd = d; if(anydefault(&d)){ /* must first stat file so we can copy current values */ t9.type = Tstat9p1; t9.tag = t->tag; t9.fid = t->fid; err = transact9p1(&t9, &r9, mdata9p1); if(err) return err; if(convM2D9p1(r9.stat, &cd) != DIRREC) return "bad in conversion in wstat"; /* fill in default values */ if(d.name[0] != '\0'){ if(strlen(d.name) >= NAMEREC) return Etoolong; cd.name = d.name; } if(d.uid[0] != '\0'){ if(strlen(d.uid) >= NAMEREC) return Etoolong; cd.uid = d.uid; } if(d.gid[0] != '\0'){ if(strlen(d.gid) >= NAMEREC) return Etoolong; cd.gid = d.gid; } if(d.mode != ~0) cd.mode = d.mode; if(d.mtime != ~0) cd.mtime = d.mtime; if(d.length != ~0LL) cd.length = d.length; } if(convD2M9p1(&cd, t9.stat) != DIRREC) return "bad out conversion in wstat"; t9.type = Twstat9p1; t9.tag = t->tag; t9.fid = t->fid; err = transact9p1(&t9, &r9, mdata9p1); if(err) return err; return 0; } void * emalloc(ulong n) { void *p; p = malloc(n); if(!p) fatal("out of memory: %r"); memset(p, 0, n); return p; } void fatal(char *fmt, ...) { char buf[1024]; va_list arg; if(fmt){ va_start(arg, fmt); vseprint(buf, buf+sizeof(buf), fmt, arg); va_end(arg); fprint(2, "%s: (pid %d) %s\n", argv0, getpid(), buf); }else buf[0] = '\0'; if(mainpid){ /* two hits are sometimes needed */ postnote(PNGROUP, mainpid, "die1 - from srvold9p"); postnote(PNGROUP, mainpid, "die2 - from srvold9p"); } exits(buf); }