
exportfs -d logs 9p traffic to /tmp/exportdb. -f allows writing to a different file. exportfs silently continues if it doesn't have permissions to create or write to /tmp/exportdb. These are poor behaviors. A better default is to write to stderr, since it is 9P debug info that is better immediately printed, and not user info that is better handled by syslog(). As a result, -f is obsolete and thus removed. Redirect responsibility is now on rc. As a side effect, rc will fail if it doesn't have permissions to write. exportfs(4) is updated to reflect all changes and with a better Synopsis. oexportfs is changed to match exportfs. oexportfs(4) is updated to reflect all changes. The Synopsis is not changed due to the number of flags. Removed -f from iostats. iostats(4) is updated to reflect all changes. ---
748 lines
12 KiB
C
748 lines
12 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <auth.h>
|
|
#include <fcall.h>
|
|
#define Extern extern
|
|
#include "exportfs.h"
|
|
|
|
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 Ereadonly[] = "File system read only";
|
|
char Enoprocs[] = "Out of processes";
|
|
|
|
ulong messagesize;
|
|
int readonly;
|
|
|
|
void
|
|
Xversion(Fsrpc *t)
|
|
{
|
|
Fcall rhdr;
|
|
|
|
if(t->work.msize < 256){
|
|
reply(&t->work, &rhdr, "version: message size too small");
|
|
putsbuf(t);
|
|
return;
|
|
}
|
|
if(t->work.msize > messagesize)
|
|
t->work.msize = messagesize;
|
|
messagesize = t->work.msize;
|
|
rhdr.msize = t->work.msize;
|
|
rhdr.version = "9P2000";
|
|
if(strncmp(t->work.version, "9P", 2) != 0)
|
|
rhdr.version = "unknown";
|
|
reply(&t->work, &rhdr, 0);
|
|
putsbuf(t);
|
|
}
|
|
|
|
void
|
|
Xauth(Fsrpc *t)
|
|
{
|
|
Fcall rhdr;
|
|
|
|
reply(&t->work, &rhdr, "exportfs: authentication not required");
|
|
putsbuf(t);
|
|
}
|
|
|
|
void
|
|
Xflush(Fsrpc *t)
|
|
{
|
|
Fcall rhdr;
|
|
Fsrpc *w;
|
|
Proc *m;
|
|
|
|
for(m = Proclist; m != nil; m = m->next){
|
|
w = m->busy;
|
|
if(w == nil || w->work.tag != t->work.oldtag)
|
|
continue;
|
|
|
|
lock(m);
|
|
w = m->busy;
|
|
if(w != nil && w->work.tag == t->work.oldtag) {
|
|
w->flushtag = t->work.tag;
|
|
DEBUG(2, "\tset flushtag %d\n", t->work.tag);
|
|
postnote(PNPROC, m->pid, "flush");
|
|
unlock(m);
|
|
putsbuf(t);
|
|
return;
|
|
}
|
|
unlock(m);
|
|
}
|
|
|
|
reply(&t->work, &rhdr, 0);
|
|
DEBUG(2, "\tflush reply\n");
|
|
putsbuf(t);
|
|
}
|
|
|
|
void
|
|
Xattach(Fsrpc *t)
|
|
{
|
|
int i, nfd;
|
|
Fcall rhdr;
|
|
Fid *f;
|
|
char buf[128];
|
|
|
|
f = newfid(t->work.fid);
|
|
if(f == nil) {
|
|
reply(&t->work, &rhdr, Ebadfid);
|
|
putsbuf(t);
|
|
return;
|
|
}
|
|
|
|
if(srvfd >= 0){
|
|
if(psmpt == nil){
|
|
Nomount:
|
|
reply(&t->work, &rhdr, Enopsmt);
|
|
freefid(t->work.fid);
|
|
putsbuf(t);
|
|
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) == -1){
|
|
errstr(buf, sizeof buf);
|
|
reply(&t->work, &rhdr, buf);
|
|
freefid(t->work.fid);
|
|
putsbuf(t);
|
|
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);
|
|
putsbuf(t);
|
|
}
|
|
|
|
Fid*
|
|
clonefid(Fid *f, int new)
|
|
{
|
|
Fid *n;
|
|
|
|
n = newfid(new);
|
|
if(n == nil) {
|
|
n = getfid(new);
|
|
if(n == nil)
|
|
fatal("inconsistent fids");
|
|
if(n->fid >= 0)
|
|
close(n->fid);
|
|
freefid(new);
|
|
n = newfid(new);
|
|
if(n == nil)
|
|
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 == nil) {
|
|
reply(&t->work, &rhdr, Ebadfid);
|
|
putsbuf(t);
|
|
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 == nil){
|
|
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);
|
|
putsbuf(t);
|
|
}
|
|
|
|
void
|
|
Xclunk(Fsrpc *t)
|
|
{
|
|
Fcall rhdr;
|
|
Fid *f;
|
|
|
|
f = getfid(t->work.fid);
|
|
if(f == nil) {
|
|
reply(&t->work, &rhdr, Ebadfid);
|
|
putsbuf(t);
|
|
return;
|
|
}
|
|
|
|
if(f->fid >= 0)
|
|
close(f->fid);
|
|
|
|
freefid(t->work.fid);
|
|
reply(&t->work, &rhdr, 0);
|
|
putsbuf(t);
|
|
}
|
|
|
|
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 == nil) {
|
|
reply(&t->work, &rhdr, Ebadfid);
|
|
putsbuf(t);
|
|
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);
|
|
putsbuf(t);
|
|
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);
|
|
putsbuf(t);
|
|
}
|
|
|
|
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);
|
|
putsbuf(t);
|
|
return;
|
|
}
|
|
f = getfid(t->work.fid);
|
|
if(f == nil) {
|
|
reply(&t->work, &rhdr, Ebadfid);
|
|
putsbuf(t);
|
|
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);
|
|
putsbuf(t);
|
|
return;
|
|
}
|
|
|
|
nf = file(f->f, t->work.name);
|
|
if(nf == nil) {
|
|
errstr(err, sizeof err);
|
|
reply(&t->work, &rhdr, err);
|
|
putsbuf(t);
|
|
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);
|
|
putsbuf(t);
|
|
}
|
|
|
|
void
|
|
Xremove(Fsrpc *t)
|
|
{
|
|
char err[ERRMAX], *path;
|
|
Fcall rhdr;
|
|
Fid *f;
|
|
|
|
if(readonly) {
|
|
reply(&t->work, &rhdr, Ereadonly);
|
|
putsbuf(t);
|
|
return;
|
|
}
|
|
f = getfid(t->work.fid);
|
|
if(f == nil) {
|
|
reply(&t->work, &rhdr, Ebadfid);
|
|
putsbuf(t);
|
|
return;
|
|
}
|
|
|
|
path = makepath(f->f, "");
|
|
DEBUG(2, "\tremove: %s\n", path);
|
|
if(remove(path) < 0) {
|
|
free(path);
|
|
errstr(err, sizeof err);
|
|
reply(&t->work, &rhdr, err);
|
|
putsbuf(t);
|
|
return;
|
|
}
|
|
free(path);
|
|
|
|
f->f->inval = 1;
|
|
if(f->fid >= 0)
|
|
close(f->fid);
|
|
freefid(t->work.fid);
|
|
|
|
reply(&t->work, &rhdr, 0);
|
|
putsbuf(t);
|
|
}
|
|
|
|
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);
|
|
putsbuf(t);
|
|
return;
|
|
}
|
|
f = getfid(t->work.fid);
|
|
if(f == nil) {
|
|
reply(&t->work, &rhdr, Ebadfid);
|
|
putsbuf(t);
|
|
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);
|
|
putsbuf(t);
|
|
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);
|
|
putsbuf(t);
|
|
}
|
|
|
|
void
|
|
slave(Fsrpc *f)
|
|
{
|
|
static int nproc;
|
|
Proc *m, **l;
|
|
Fcall rhdr;
|
|
int pid;
|
|
|
|
if(readonly){
|
|
switch(f->work.type){
|
|
case Twrite:
|
|
reply(&f->work, &rhdr, Ereadonly);
|
|
putsbuf(f);
|
|
return;
|
|
case Topen:
|
|
if((f->work.mode&3) == OWRITE || (f->work.mode&(OTRUNC|ORCLOSE))){
|
|
reply(&f->work, &rhdr, Ereadonly);
|
|
putsbuf(f);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
for(;;) {
|
|
for(l = &Proclist; (m = *l) != nil; l = &m->next) {
|
|
if(m->busy != nil)
|
|
continue;
|
|
|
|
m->busy = f;
|
|
while(rendezvous(m, f) == (void*)~0)
|
|
;
|
|
|
|
/* swept a slave proc */
|
|
if(f == nil){
|
|
*l = m->next;
|
|
free(m);
|
|
nproc--;
|
|
break;
|
|
}
|
|
f = nil;
|
|
|
|
/*
|
|
* as long as the number of slave procs
|
|
* is small, dont bother sweeping.
|
|
*/
|
|
if(nproc < 16)
|
|
break;
|
|
}
|
|
if(f == nil)
|
|
return;
|
|
|
|
m = emallocz(sizeof(Proc));
|
|
pid = rfork(RFPROC|RFMEM|RFNOWAIT);
|
|
switch(pid) {
|
|
case -1:
|
|
reply(&f->work, &rhdr, Enoprocs);
|
|
putsbuf(f);
|
|
free(m);
|
|
return;
|
|
|
|
case 0:
|
|
blockingslave(m);
|
|
_exits(0);
|
|
|
|
default:
|
|
m->pid = pid;
|
|
m->next = Proclist;
|
|
Proclist = m;
|
|
nproc++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
blockingslave(Proc *m)
|
|
{
|
|
Fsrpc *p;
|
|
Fcall rhdr;
|
|
|
|
notify(flushaction);
|
|
|
|
for(;;) {
|
|
p = rendezvous(m, nil);
|
|
if(p == (void*)~0) /* Interrupted */
|
|
continue;
|
|
if(p == nil) /* Swept */
|
|
break;
|
|
|
|
DEBUG(2, "\tslave: %d %F\n", m->pid, &p->work);
|
|
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");
|
|
}
|
|
flushme:
|
|
lock(m);
|
|
m->busy = nil;
|
|
unlock(m);
|
|
|
|
/* no more flushes can come in now */
|
|
if(p->flushtag != NOTAG) {
|
|
p->work.type = Tflush;
|
|
p->work.tag = p->flushtag;
|
|
reply(&p->work, &rhdr, 0);
|
|
}
|
|
putsbuf(p);
|
|
}
|
|
}
|
|
|
|
int
|
|
openmount(int sfd)
|
|
{
|
|
int p[2], fd, i, n;
|
|
char *arg[10], fdbuf[20], mbuf[20];
|
|
Dir *dir;
|
|
|
|
if(pipe(p) < 0)
|
|
return -1;
|
|
|
|
switch(rfork(RFPROC|RFMEM|RFNOWAIT|RFNAMEG|RFFDG|RFREND)){
|
|
case -1:
|
|
close(p[0]);
|
|
close(p[1]);
|
|
return -1;
|
|
|
|
default:
|
|
close(sfd);
|
|
close(p[0]);
|
|
return p[1];
|
|
|
|
case 0:
|
|
break;
|
|
}
|
|
|
|
dup(p[0], 0);
|
|
dup(p[0], 1);
|
|
|
|
/* close all remaining file descriptors except sfd */
|
|
if((fd = open("/fd", OREAD)) < 0)
|
|
_exits("open /fd failed");
|
|
n = dirreadall(fd, &dir);
|
|
for(i=0; i<n; i++){
|
|
if(strstr(dir[i].name, "ctl"))
|
|
continue;
|
|
fd = atoi(dir[i].name);
|
|
if(fd > 2 && fd != sfd)
|
|
close(fd);
|
|
}
|
|
free(dir);
|
|
|
|
arg[0] = argv0; /* "/bin/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;
|
|
|
|
exec(arg[0], arg);
|
|
arg[0] = "/bin/exportfs";
|
|
exec(arg[0], 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 == nil) {
|
|
reply(work, &rhdr, Ebadfid);
|
|
return;
|
|
}
|
|
if(f->fid >= 0) {
|
|
close(f->fid);
|
|
f->fid = -1;
|
|
}
|
|
|
|
path = makepath(f->f, "");
|
|
DEBUG(2, "\topen: %s %d\n", path, work->mode);
|
|
f->fid = open(path, work->mode);
|
|
free(path);
|
|
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(2, "\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 == nil) {
|
|
reply(work, &rhdr, Ebadfid);
|
|
return;
|
|
}
|
|
|
|
n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count;
|
|
data = malloc(n);
|
|
if(data == nil) {
|
|
reply(work, &rhdr, Enomem);
|
|
return;
|
|
}
|
|
|
|
/* 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);
|
|
if(r < 0) {
|
|
free(data);
|
|
errstr(err, sizeof err);
|
|
reply(work, &rhdr, err);
|
|
return;
|
|
}
|
|
DEBUG(2, "\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 == nil) {
|
|
reply(work, &rhdr, Ebadfid);
|
|
return;
|
|
}
|
|
|
|
n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count;
|
|
n = pwrite(f->fid, work->data, n, work->offset);
|
|
if(n < 0) {
|
|
errstr(err, sizeof err);
|
|
reply(work, &rhdr, err);
|
|
return;
|
|
}
|
|
|
|
DEBUG(2, "\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);
|
|
}
|