#include #include #include #include #define Extern extern #include "exportfs.h" extern char *netdir, *local, *remote; char Ebadfid[] = "Bad fid"; char Enotdir[] = "Not a directory"; char Edupfid[] = "Fid already in use"; char Eopen[] = "Fid already opened"; char Exmnt[] = "Cannot .. past mount point"; char Emip[] = "Mount in progress"; char Enopsmt[] = "Out of pseudo mount points"; char Enomem[] = "No memory"; char Eversion[] = "Bad 9P2000 version"; char Ereadonly[] = "File system read only"; ulong messagesize; int readonly; void Xversion(Fsrpc *t) { Fcall rhdr; if(t->work.msize > messagesize) t->work.msize = messagesize; messagesize = t->work.msize; if(strncmp(t->work.version, "9P2000", 6) != 0){ reply(&t->work, &rhdr, Eversion); return; } rhdr.version = "9P2000"; rhdr.msize = t->work.msize; reply(&t->work, &rhdr, 0); t->busy = 0; } void Xauth(Fsrpc *t) { Fcall rhdr; reply(&t->work, &rhdr, "exportfs: authentication not required"); t->busy = 0; } void Xflush(Fsrpc *t) { Fsrpc *w, *e; Fcall rhdr; e = &Workq[Nr_workbufs]; for(w = Workq; w < e; w++) { if(w->work.tag == t->work.oldtag) { DEBUG(DFD, "\tQ busy %d pid %p can %d\n", w->busy, w->pid, w->canint); if(w->busy && w->pid) { w->flushtag = t->work.tag; DEBUG(DFD, "\tset flushtag %d\n", t->work.tag); if(w->canint) postnote(PNPROC, w->pid, "flush"); t->busy = 0; return; } } } reply(&t->work, &rhdr, 0); DEBUG(DFD, "\tflush reply\n"); t->busy = 0; } void Xattach(Fsrpc *t) { int i, nfd; Fcall rhdr; Fid *f; char buf[128]; f = newfid(t->work.fid); if(f == 0) { reply(&t->work, &rhdr, Ebadfid); t->busy = 0; return; } if(srvfd >= 0){ if(psmpt == 0){ Nomount: reply(&t->work, &rhdr, Enopsmt); t->busy = 0; freefid(t->work.fid); return; } for(i=0; i= Npsmpt) goto Nomount; sprint(buf, "%d", i); f->f = file(psmpt, buf); if(f->f == nil) goto Nomount; sprint(buf, "/mnt/exportfs/%d", i); nfd = dup(srvfd, -1); if(amount(nfd, buf, MREPL|MCREATE, t->work.aname) < 0){ errstr(buf, sizeof buf); reply(&t->work, &rhdr, buf); t->busy = 0; freefid(t->work.fid); close(nfd); return; } psmap[i] = 1; f->mid = i; }else{ f->f = root; f->f->ref++; } rhdr.qid = f->f->qid; reply(&t->work, &rhdr, 0); t->busy = 0; } Fid* clonefid(Fid *f, int new) { Fid *n; n = newfid(new); if(n == 0) { n = getfid(new); if(n == 0) fatal("inconsistent fids"); if(n->fid >= 0) close(n->fid); freefid(new); n = newfid(new); if(n == 0) fatal("inconsistent fids2"); } n->f = f->f; n->f->ref++; return n; } void Xwalk(Fsrpc *t) { char err[ERRMAX], *e; Fcall rhdr; Fid *f, *nf; File *wf; int i; f = getfid(t->work.fid); if(f == 0) { reply(&t->work, &rhdr, Ebadfid); t->busy = 0; return; } nf = nil; if(t->work.newfid != t->work.fid){ nf = clonefid(f, t->work.newfid); f = nf; } rhdr.nwqid = 0; e = nil; for(i=0; iwork.nwname; i++){ if(i == MAXWELEM){ e = "Too many path elements"; break; } if(strcmp(t->work.wname[i], "..") == 0) { if(f->f->parent == nil) { e = Exmnt; break; } wf = f->f->parent; wf->ref++; goto Accept; } wf = file(f->f, t->work.wname[i]); if(wf == 0){ errstr(err, sizeof err); e = err; break; } Accept: freefile(f->f); rhdr.wqid[rhdr.nwqid++] = wf->qid; f->f = wf; continue; } if(nf!=nil && (e!=nil || rhdr.nwqid!=t->work.nwname)) freefid(t->work.newfid); if(rhdr.nwqid > 0) e = nil; reply(&t->work, &rhdr, e); t->busy = 0; } void Xclunk(Fsrpc *t) { Fcall rhdr; Fid *f; f = getfid(t->work.fid); if(f == 0) { reply(&t->work, &rhdr, Ebadfid); t->busy = 0; return; } if(f->fid >= 0) close(f->fid); freefid(t->work.fid); reply(&t->work, &rhdr, 0); t->busy = 0; } void Xstat(Fsrpc *t) { char err[ERRMAX], *path; Fcall rhdr; Fid *f; Dir *d; int s; uchar *statbuf; f = getfid(t->work.fid); if(f == 0) { reply(&t->work, &rhdr, Ebadfid); t->busy = 0; return; } if(f->fid >= 0) d = dirfstat(f->fid); else { path = makepath(f->f, ""); d = dirstat(path); free(path); } if(d == nil) { errstr(err, sizeof err); reply(&t->work, &rhdr, err); t->busy = 0; return; } d->qid.path = f->f->qidt->uniqpath; s = sizeD2M(d); statbuf = emallocz(s); s = convD2M(d, statbuf, s); free(d); rhdr.nstat = s; rhdr.stat = statbuf; reply(&t->work, &rhdr, 0); free(statbuf); t->busy = 0; } static int getiounit(int fd) { int n; n = iounit(fd); if(n > messagesize-IOHDRSZ) n = messagesize-IOHDRSZ; return n; } void Xcreate(Fsrpc *t) { char err[ERRMAX], *path; Fcall rhdr; Fid *f; File *nf; if(readonly) { reply(&t->work, &rhdr, Ereadonly); t->busy = 0; return; } f = getfid(t->work.fid); if(f == 0) { reply(&t->work, &rhdr, Ebadfid); t->busy = 0; return; } path = makepath(f->f, t->work.name); f->fid = create(path, t->work.mode, t->work.perm); free(path); if(f->fid < 0) { errstr(err, sizeof err); reply(&t->work, &rhdr, err); t->busy = 0; return; } nf = file(f->f, t->work.name); if(nf == 0) { errstr(err, sizeof err); reply(&t->work, &rhdr, err); t->busy = 0; return; } f->mode = t->work.mode; freefile(f->f); f->f = nf; rhdr.qid = f->f->qid; rhdr.iounit = getiounit(f->fid); reply(&t->work, &rhdr, 0); t->busy = 0; } void Xremove(Fsrpc *t) { char err[ERRMAX], *path; Fcall rhdr; Fid *f; if(readonly) { reply(&t->work, &rhdr, Ereadonly); t->busy = 0; return; } f = getfid(t->work.fid); if(f == 0) { reply(&t->work, &rhdr, Ebadfid); t->busy = 0; return; } path = makepath(f->f, ""); DEBUG(DFD, "\tremove: %s\n", path); if(remove(path) < 0) { free(path); errstr(err, sizeof err); reply(&t->work, &rhdr, err); t->busy = 0; return; } free(path); f->f->inval = 1; if(f->fid >= 0) close(f->fid); freefid(t->work.fid); reply(&t->work, &rhdr, 0); t->busy = 0; } void Xwstat(Fsrpc *t) { char err[ERRMAX], *path; Fcall rhdr; Fid *f; int s; char *strings; Dir d; if(readonly) { reply(&t->work, &rhdr, Ereadonly); t->busy = 0; return; } f = getfid(t->work.fid); if(f == 0) { reply(&t->work, &rhdr, Ebadfid); t->busy = 0; return; } strings = emallocz(t->work.nstat); /* ample */ if(convM2D(t->work.stat, t->work.nstat, &d, strings) <= BIT16SZ){ rerrstr(err, sizeof err); reply(&t->work, &rhdr, err); t->busy = 0; free(strings); return; } if(f->fid >= 0) s = dirfwstat(f->fid, &d); else { path = makepath(f->f, ""); s = dirwstat(path, &d); free(path); } if(s < 0) { rerrstr(err, sizeof err); reply(&t->work, &rhdr, err); } else { /* wstat may really be rename */ if(strcmp(d.name, f->f->name)!=0 && strcmp(d.name, "")!=0){ free(f->f->name); f->f->name = estrdup(d.name); } reply(&t->work, &rhdr, 0); } free(strings); t->busy = 0; } /* * based on libthread's threadsetname, but drags in less library code. * actually just sets the arguments displayed. */ void procsetname(char *fmt, ...) { int fd; char *cmdname; char buf[128]; va_list arg; va_start(arg, fmt); cmdname = vsmprint(fmt, arg); va_end(arg); if (cmdname == nil) return; snprint(buf, sizeof buf, "#p/%d/args", getpid()); if((fd = open(buf, OWRITE)) >= 0){ write(fd, cmdname, strlen(cmdname)+1); close(fd); } free(cmdname); } void slave(Fsrpc *f) { Proc *p; uintptr pid; Fcall rhdr; static int nproc; if(readonly){ switch(f->work.type){ case Twrite: reply(&f->work, &rhdr, Ereadonly); f->busy = 0; return; case Topen: if((f->work.mode&3) == OWRITE || (f->work.mode&OTRUNC)){ reply(&f->work, &rhdr, Ereadonly); f->busy = 0; return; } } } for(;;) { for(p = Proclist; p; p = p->next) { if(p->busy == 0) { f->pid = p->pid; p->busy = 1; pid = (uintptr)rendezvous((void*)p->pid, f); if(pid != p->pid) fatal("rendezvous sync fail"); return; } } if(++nproc > MAXPROC) fatal("too many procs"); pid = rfork(RFPROC|RFMEM); switch(pid) { case -1: fatal("rfork"); case 0: if (local[0] != '\0') if (netdir[0] != '\0') procsetname("%s: %s -> %s", netdir, local, remote); else procsetname("%s -> %s", local, remote); blockingslave(); fatal("slave"); default: p = malloc(sizeof(Proc)); if(p == 0) fatal("out of memory"); p->busy = 0; p->pid = pid; p->next = Proclist; Proclist = p; rendezvous((void*)pid, p); } } } void blockingslave(void) { Fsrpc *p; Fcall rhdr; Proc *m; uintptr pid; notify(flushaction); pid = getpid(); m = rendezvous((void*)pid, 0); for(;;) { p = rendezvous((void*)pid, (void*)pid); if(p == (void*)~0) /* Interrupted */ continue; DEBUG(DFD, "\tslave: %p %F b %d p %p\n", pid, &p->work, p->busy, p->pid); if(p->flushtag != NOTAG) goto flushme; switch(p->work.type) { case Tread: slaveread(p); break; case Twrite: slavewrite(p); break; case Topen: slaveopen(p); break; default: reply(&p->work, &rhdr, "exportfs: slave type error"); } if(p->flushtag != NOTAG) { flushme: p->work.type = Tflush; p->work.tag = p->flushtag; reply(&p->work, &rhdr, 0); } p->busy = 0; m->busy = 0; } } int openmount(int sfd) { int p[2]; char *arg[10], fdbuf[20], mbuf[20]; if(pipe(p) < 0) return -1; switch(rfork(RFPROC|RFMEM|RFNOWAIT|RFNAMEG|RFFDG)){ case -1: return -1; default: close(sfd); close(p[0]); return p[1]; case 0: break; } close(p[1]); arg[0] = "exportfs"; snprint(fdbuf, sizeof fdbuf, "-S/fd/%d", sfd); arg[1] = fdbuf; snprint(mbuf, sizeof mbuf, "-m%lud", messagesize-IOHDRSZ); arg[2] = mbuf; arg[3] = nil; close(0); close(1); dup(p[0], 0); dup(p[0], 1); exec("/bin/exportfs", arg); _exits("whoops: exec failed"); return -1; } void slaveopen(Fsrpc *p) { char err[ERRMAX], *path; Fcall *work, rhdr; Fid *f; Dir *d; work = &p->work; f = getfid(work->fid); if(f == 0) { reply(work, &rhdr, Ebadfid); return; } if(f->fid >= 0) { close(f->fid); f->fid = -1; } path = makepath(f->f, ""); DEBUG(DFD, "\topen: %s %d\n", path, work->mode); p->canint = 1; if(p->flushtag != NOTAG){ free(path); return; } /* There is a race here I ignore because there are no locks */ f->fid = open(path, work->mode); free(path); p->canint = 0; if(f->fid < 0 || (d = dirfstat(f->fid)) == nil) { Error: errstr(err, sizeof err); reply(work, &rhdr, err); return; } f->f->qid = d->qid; free(d); if(f->f->qid.type & QTMOUNT){ /* fork new exportfs for this */ f->fid = openmount(f->fid); if(f->fid < 0) goto Error; } DEBUG(DFD, "\topen: fd %d\n", f->fid); f->mode = work->mode; f->offset = 0; rhdr.iounit = getiounit(f->fid); rhdr.qid = f->f->qid; reply(work, &rhdr, 0); } void slaveread(Fsrpc *p) { Fid *f; int n, r; Fcall *work, rhdr; char *data, err[ERRMAX]; work = &p->work; f = getfid(work->fid); if(f == 0) { reply(work, &rhdr, Ebadfid); return; } n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count; p->canint = 1; if(p->flushtag != NOTAG) return; data = malloc(n); if(data == nil) fatal(Enomem); /* can't just call pread, since directories must update the offset */ if(patternfile != nil && (f->f->qid.type&QTDIR)) r = preaddir(f, (uchar*)data, n, work->offset); else r = pread(f->fid, data, n, work->offset); p->canint = 0; if(r < 0) { free(data); errstr(err, sizeof err); reply(work, &rhdr, err); return; } DEBUG(DFD, "\tread: fd=%d %d bytes\n", f->fid, r); rhdr.data = data; rhdr.count = r; reply(work, &rhdr, 0); free(data); } void slavewrite(Fsrpc *p) { char err[ERRMAX]; Fcall *work, rhdr; Fid *f; int n; work = &p->work; f = getfid(work->fid); if(f == 0) { reply(work, &rhdr, Ebadfid); return; } n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count; p->canint = 1; if(p->flushtag != NOTAG) return; n = pwrite(f->fid, work->data, n, work->offset); p->canint = 0; if(n < 0) { errstr(err, sizeof err); reply(work, &rhdr, err); return; } DEBUG(DFD, "\twrite: %d bytes fd=%d\n", n, f->fid); rhdr.count = n; reply(work, &rhdr, 0); } void reopen(Fid *f) { USED(f); fatal("reopen"); } void flushaction(void *a, char *cause) { USED(a); if(strncmp(cause, "sys:", 4) == 0 && !strstr(cause, "pipe")) { fprint(2, "exportsrv: note: %s\n", cause); exits("noted"); } if(strncmp(cause, "kill", 4) == 0) noted(NDFLT); noted(NCONT); }