jehanne/sys/src/cmd/exportfs/exportsrv.c

769 lines
13 KiB
C

/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#include <u.h>
#include <lib9.h>
#include <auth.h>
#include <9P2000.h>
#include "exportfs.h"
extern char *netdir, *local, *remote;
static char Ebadfid[] = "bad fid";
static char Enopsmt[] = "out of pseudo mount points";
static char Ereadonly[] = "file system read only";
static char Eversion[] = "bad 9P2000 version";
static char Exmnt[] = "cannot .. past mount point";
uint32_t 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; i++)
if(psmap[i] == 0)
break;
if(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){
sys_errstr(buf, sizeof buf);
reply(&t->work, &rhdr, buf);
t->busy = 0;
freefid(t->work.fid);
sys_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)
sys_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; i<t->work.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){
sys_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)
sys_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;
uint8_t *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) {
sys_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 = ocreate(path, t->work.mode, t->work.perm);
free(path);
if(f->fid < 0) {
sys_errstr(err, sizeof err);
reply(&t->work, &rhdr, err);
t->busy = 0;
return;
}
nf = file(f->f, t->work.name);
if(nf == 0) {
sys_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(sys_remove(path) < 0) {
free(path);
sys_errstr(err, sizeof err);
reply(&t->work, &rhdr, err);
t->busy = 0;
return;
}
free(path);
f->f->inval = 1;
if(f->fid >= 0)
sys_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 = sys_open(buf, OWRITE)) >= 0){
jehanne_write(fd, cmdname, strlen(cmdname)+1);
sys_close(fd);
}
free(cmdname);
}
void
slave(Fsrpc *f)
{
Proc *p;
uintptr_t pid;
Fcall rhdr;
static int nproc;
if(readonly){
switch(f->work.type){
default:
break;
case Twrite:
reply(&f->work, &rhdr, Ereadonly);
f->busy = 0;
return;
case Topen:
if((f->work.mode&3) == NP_OWRITE || (f->work.mode&NP_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_t)sys_rendezvous((void*)p->pid, f);
if(pid != p->pid)
fatal("rendezvous sync fail");
return;
}
}
if(++nproc > MAXPROC)
fatal("too many procs");
pid = sys_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(Enomem);
p->busy = 0;
p->pid = pid;
p->next = Proclist;
Proclist = p;
sys_rendezvous((void*)pid, p);
}
}
}
void
blockingslave(void)
{
Fsrpc *p;
Fcall rhdr;
Proc *m;
uintptr_t pid;
sys_notify(flushaction);
pid = getpid();
m = sys_rendezvous((void*)pid, 0);
for(;;) {
p = sys_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(sys_rfork(RFPROC|RFMEM|RFNOWAIT|RFNAMEG|RFFDG)){
case -1:
return -1;
default:
sys_close(sfd);
sys_close(p[0]);
return p[1];
case 0:
break;
}
sys_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;
sys_close(0);
sys_close(1);
dup(p[0], 0);
dup(p[0], 1);
sys_exec("/cmd/exportfs", (const char**)arg);
sys__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) {
sys_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 = sys_open(path, work->mode);
free(path);
p->canint = 0;
if(f->fid < 0 || (d = dirfstat(f->fid)) == nil) {
Error:
sys_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, (uint8_t*)data, n, work->offset);
else
r = sys_pread(f->fid, data, n, work->offset);
p->canint = 0;
if(r < 0) {
free(data);
sys_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 = sys_pwrite(f->fid, work->data, n, work->offset);
p->canint = 0;
if(n < 0) {
sys_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)
sys_noted(NDFLT);
sys_noted(NCONT);
}