#include #include #include #include #include "dat.h" #include "fns.h" enum { Maxfdata = 8192, Maxiosize = IOHDRSZ+Maxfdata, }; void io(int); void rversion(void); void rattach(void); void rauth(void); void rclunk(void); void rcreate(void); void rflush(void); void ropen(void); void rread(void); void rremove(void); void rsession(void); void rstat(void); void rwalk(void); void rwrite(void); void rwstat(void); static int openflags(int); static void rmservice(void); static void usage(void); #define Reqsize (sizeof(Fcall)+Maxfdata) Fcall *req; Fcall *rep; uchar mdata[Maxiosize]; char fdata[Maxfdata]; uchar statbuf[STATMAX]; int errno; static char srvfile[64]; extern Xfsub *xsublist[]; extern int nclust; jmp_buf err_lab[16]; int nerr_lab; char err_msg[ERRMAX]; int chatty; int nojoliet; int noplan9; int norock; void (*fcalls[])(void) = { [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, }; void main(int argc, char **argv) { int srvfd, pipefd[2], stdio; Xfsub **xs; stdio = 0; ARGBEGIN { case '9': noplan9 = 1; break; case 'c': nclust = atoi(EARGF(usage())); if (nclust <= 0) sysfatal("nclust %d non-positive", nclust); break; case 'f': deffile = EARGF(usage()); break; case 'r': norock = 1; break; case 's': stdio = 1; break; case 'v': chatty = 1; break; case 'J': nojoliet = 1; break; default: usage(); } ARGEND switch(argc) { case 0: break; case 1: srvname = argv[0]; break; default: usage(); } iobuf_init(); for(xs=xsublist; *xs; xs++) (*(*xs)->reset)(); if(stdio) { pipefd[0] = 0; pipefd[1] = 1; } else { close(0); close(1); open("/dev/null", OREAD); open("/dev/null", OWRITE); if(pipe(pipefd) < 0) panic(1, "pipe"); sprint(srvfile, "/srv/%s", srvname); srvfd = create(srvfile, OWRITE|ORCLOSE, 0600); if(srvfd < 0) panic(1, srvfile); fprint(srvfd, "%d", pipefd[0]); close(pipefd[0]); fprint(2, "%s %d: serving %s\n", argv0, getpid(), srvfile); } srvfd = pipefd[1]; switch(rfork(RFNOWAIT|RFNOTEG|RFFDG|RFPROC)){ case -1: panic(1, "fork"); default: _exits(0); case 0: break; } io(srvfd); exits(0); } void io(int srvfd) { int n, pid; Fcall xreq, xrep; req = &xreq; rep = &xrep; pid = getpid(); fmtinstall('F', fcallfmt); for(;;){ /* * reading from a pipe or a network device * will give an error after a few eof reads. * however, we cannot tell the difference * between a zero-length read and an interrupt * on the processes writing to us, * so we wait for the error. */ n = read9pmsg(srvfd, mdata, sizeof mdata); if(n < 0) break; if(n == 0) continue; if(convM2S(mdata, n, req) == 0) continue; if(chatty) fprint(2, "9660srv %d:<-%F\n", pid, req); errno = 0; if(!waserror()){ err_msg[0] = 0; if(req->type >= nelem(fcalls) || !fcalls[req->type]) error("bad fcall type"); (*fcalls[req->type])(); poperror(); } if(err_msg[0]){ rep->type = Rerror; rep->ename = err_msg; }else{ rep->type = req->type + 1; rep->fid = req->fid; } rep->tag = req->tag; if(chatty) fprint(2, "9660srv %d:->%F\n", pid, rep); n = convS2M(rep, mdata, sizeof mdata); if(n == 0) panic(1, "convS2M error on write"); if(write(srvfd, mdata, n) != n) panic(1, "mount write"); if(nerr_lab != 0) panic(0, "err stack %d: %lux %lux %lux %lux %lux %lux", nerr_lab, err_lab[0][JMPBUFPC], err_lab[1][JMPBUFPC], err_lab[2][JMPBUFPC], err_lab[3][JMPBUFPC], err_lab[4][JMPBUFPC], err_lab[5][JMPBUFPC]); } chat("server shut down"); } static void usage(void) { fprint(2, "usage: %s [-v] [-9Jr] [-s] [-f devicefile] [srvname]\n", argv0); exits("usage"); } void error(char *p) { strecpy(err_msg, err_msg+sizeof err_msg, p); nexterror(); } void nexterror(void) { longjmp(err_lab[--nerr_lab], 1); } void* ealloc(long n) { void *p; p = malloc(n); if(p == 0) error("no memory"); return p; } void setnames(Dir *d, char *n) { d->name = n; d->uid = n+Maxname; d->gid = n+Maxname*2; d->muid = n+Maxname*3; d->name[0] = '\0'; d->uid[0] = '\0'; d->gid[0] = '\0'; d->muid[0] = '\0'; } void rversion(void) { if(req->msize > Maxiosize) rep->msize = Maxiosize; else rep->msize = req->msize; rep->version = "9P2000"; } void rauth(void) { error("9660srv: authentication not required"); } void rflush(void) { } void rattach(void) { Xfs *xf; Xfile *root; Xfsub **xs; chat("attach(fid=%d,uname=\"%s\",aname=\"%s\")...", req->fid, req->uname, req->aname); if(waserror()){ xfile(req->fid, Clunk); nexterror(); } root = xfile(req->fid, Clean); root->qid = (Qid){0, 0, QTDIR}; root->xf = xf = ealloc(sizeof(Xfs)); memset(xf, 0, sizeof(Xfs)); xf->ref = 1; xf->d = getxdata(req->aname); for(xs=xsublist; *xs; xs++) if((*(*xs)->attach)(root) >= 0){ poperror(); xf->s = *xs; xf->rootqid = root->qid; rep->qid = root->qid; return; } error("unknown format"); } Xfile* doclone(Xfile *of, int newfid) { Xfile *nf, *next; nf = xfile(newfid, Clean); if(waserror()){ xfile(newfid, Clunk); nexterror(); } next = nf->next; *nf = *of; nf->next = next; nf->fid = newfid; refxfs(nf->xf, 1); if(nf->len){ nf->ptr = ealloc(nf->len); memmove(nf->ptr, of->ptr, nf->len); }else nf->ptr = of->ptr; (*of->xf->s->clone)(of, nf); poperror(); return nf; } void rwalk(void) { Xfile *f, *nf; Isofile *oldptr; int oldlen; Qid oldqid; rep->nwqid = 0; nf = nil; f = xfile(req->fid, Asis); if(req->fid != req->newfid) f = nf = doclone(f, req->newfid); /* save old state in case of error */ oldqid = f->qid; oldlen = f->len; oldptr = f->ptr; if(oldlen){ oldptr = ealloc(oldlen); memmove(oldptr, f->ptr, oldlen); } if(waserror()){ /* * if nf != nil, nf == f, which is derived from req->newfid, * so we can't clunk req->newfid with xfile, which would put * f back on the free list, until we're done with f below. */ if(rep->nwqid == req->nwname){ if(oldlen) free(oldptr); }else{ /* restore previous state */ f->qid = oldqid; if(f->len) free(f->ptr); f->ptr = oldptr; f->len = oldlen; } if(nf != nil) xfile(req->newfid, Clunk); if(rep->nwqid==req->nwname || rep->nwqid > 0){ err_msg[0] = '\0'; return; } nexterror(); } for(rep->nwqid=0; rep->nwqid < req->nwname && rep->nwqid < MAXWELEM; rep->nwqid++){ chat("\twalking %s\n", req->wname[rep->nwqid]); if(!(f->qid.type & QTDIR)){ chat("\tnot dir: type=%#x\n", f->qid.type); error("walk in non-directory"); } if(strcmp(req->wname[rep->nwqid], "..")==0){ if(f->qid.path != f->xf->rootqid.path) (*f->xf->s->walkup)(f); }else (*f->xf->s->walk)(f, req->wname[rep->nwqid]); rep->wqid[rep->nwqid] = f->qid; } poperror(); if(oldlen) free(oldptr); } void ropen(void) { Xfile *f; f = xfile(req->fid, Asis); if(f->flags&Omodes) error("open on open file"); if(req->mode&ORCLOSE) error("no removes"); (*f->xf->s->open)(f, req->mode); f->flags = openflags(req->mode); rep->qid = f->qid; rep->iounit = 0; } void rcreate(void) { error("no creates"); /* Xfile *f; if(strcmp(req->name, ".") == 0 || strcmp(req->name, "..") == 0) error("create . or .."); f = xfile(req->fid, Asis); if(f->flags&Omodes) error("create on open file"); if(!(f->qid.path&CHDIR)) error("create in non-directory"); (*f->xf->s->create)(f, req->name, req->perm, req->mode); chat("f->qid=0x%8.8lux...", f->qid.path); f->flags = openflags(req->mode); rep->qid = f->qid; */ } void rread(void) { Xfile *f; f=xfile(req->fid, Asis); if (!(f->flags&Oread)) error("file not opened for reading"); if(f->qid.type & QTDIR) rep->count = (*f->xf->s->readdir)(f, (uchar*)fdata, req->offset, req->count); else rep->count = (*f->xf->s->read)(f, fdata, req->offset, req->count); rep->data = fdata; } void rwrite(void) { Xfile *f; f=xfile(req->fid, Asis); if(!(f->flags&Owrite)) error("file not opened for writing"); rep->count = (*f->xf->s->write)(f, req->data, req->offset, req->count); } void rclunk(void) { Xfile *f; if(!waserror()){ f = xfile(req->fid, Asis); (*f->xf->s->clunk)(f); poperror(); } xfile(req->fid, Clunk); } void rremove(void) { error("no removes"); } void rstat(void) { Xfile *f; Dir dir; chat("stat(fid=%d)...", req->fid); f=xfile(req->fid, Asis); setnames(&dir, fdata); (*f->xf->s->stat)(f, &dir); if(chatty) showdir(2, &dir); rep->nstat = convD2M(&dir, statbuf, sizeof statbuf); rep->stat = statbuf; } void rwstat(void) { error("no wstat"); } static int openflags(int mode) { int flags = 0; switch(mode & ~(OTRUNC|OCEXEC|ORCLOSE)){ case OREAD: case OEXEC: flags = Oread; break; case OWRITE: flags = Owrite; break; case ORDWR: flags = Oread|Owrite; break; } if(mode & ORCLOSE) flags |= Orclose; return flags; } void showdir(int fd, Dir *s) { char a_time[32], m_time[32]; char *p; strcpy(a_time, ctime(s->atime)); if(p=strchr(a_time, '\n')) /* assign = */ *p = 0; strcpy(m_time, ctime(s->mtime)); if(p=strchr(m_time, '\n')) /* assign = */ *p = 0; fprint(fd, "name=\"%s\" qid=(0x%llux,%lud) type=%d dev=%d \ mode=0x%8.8lux=0%luo atime=%s mtime=%s length=%lld uid=\"%s\" gid=\"%s\"...", s->name, s->qid.path, s->qid.vers, s->type, s->dev, s->mode, s->mode, a_time, m_time, s->length, s->uid, s->gid); } #define SIZE 1024 void chat(char *fmt, ...) { va_list arg; if(chatty){ va_start(arg, fmt); vfprint(2, fmt, arg); va_end(arg); } } void panic(int rflag, char *fmt, ...) { va_list arg; char buf[SIZE]; int n; n = sprint(buf, "%s %d: ", argv0, getpid()); va_start(arg, fmt); vseprint(buf+n, buf+SIZE, fmt, arg); va_end(arg); fprint(2, (rflag ? "%s: %r\n" : "%s\n"), buf); if(chatty){ fprint(2, "abort\n"); abort(); } exits("panic"); }